mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-13 06:43:35 +00:00
Add branch labels for loops; using list
This commit is contained in:
@@ -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
|
||||
|
||||
284
code/demo.odin
284
code/demo.odin
@@ -10,40 +10,18 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("Here");
|
||||
when false {
|
||||
/*
|
||||
Version 0.1.1
|
||||
|
||||
Added:
|
||||
* Dynamic Arrays `[dynamic]Type`
|
||||
* Dynamic Maps `map[Key]Value`
|
||||
* Dynamic array and map literals
|
||||
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
|
||||
* Allow `_` in numbers
|
||||
* Variadic `append`
|
||||
* fmt.sprint*
|
||||
* Entities prefixes with an underscore do not get exported on imports
|
||||
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
|
||||
* enum types have an implict `names` field, a []string of all the names in that enum
|
||||
* immutable variables are "completely immutable" - rules need a full explanation
|
||||
* `slice_to_bytes` - convert any slice to a slice of bytes
|
||||
* `union_cast` allows for optional ok check
|
||||
* Record type field `names` (struct/raw_union/enum)
|
||||
* ?: ternary operator
|
||||
* Unions with variants and common fields
|
||||
* New built-in procedures
|
||||
- `delete` to delete map entries `delete(m, key)`
|
||||
- `clear` to clear dynamic maps and arrays `clear(map_or_array)`
|
||||
- `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
|
||||
* Unexported entities and fields using an underscore prefix
|
||||
* Unexported entities and fields using an underscore prefix
|
||||
- See `sync.odin` and explain
|
||||
|
||||
Removed:
|
||||
* Maybe/option types
|
||||
* Remove `type` keyword and other "reserved" keywords
|
||||
* `compile_assert` and `assert`return the value of the condition for semantic reasons
|
||||
* ..< and ... removed and replace with .. (half-closed range)
|
||||
|
||||
Changed:
|
||||
* `compile_assert` and `assert`return the value of the condition for semantic reasons
|
||||
* thread_local -> #thread_local
|
||||
* #include -> #load
|
||||
* Files only get checked if they are actually used
|
||||
@@ -51,20 +29,7 @@ when false {
|
||||
* Version numbering now starts from 0.1.0 and uses the convention:
|
||||
- major.minor.patch
|
||||
* Core library additions to Windows specific stuff
|
||||
|
||||
Fixes:
|
||||
* Many fmt.* fixes
|
||||
* Overloading bug due to comparison of named types
|
||||
* Overloading bug due to `#import .` collision
|
||||
* disallow a `cast` from pointers of unions
|
||||
* Minor bugs in generated IR code for slices
|
||||
|
||||
To come very Soon™:
|
||||
* Linux and OS X builds (unofficial ones do exist already)
|
||||
*/
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
{
|
||||
Fruit :: enum {
|
||||
@@ -75,6 +40,147 @@ when false {
|
||||
fmt.println(Fruit.names);
|
||||
}
|
||||
|
||||
{
|
||||
A :: struct {x, y: f32};
|
||||
B :: struct #align 16 {x, y: f32};
|
||||
fmt.println("align_of(A) =", align_of(A));
|
||||
fmt.println("align_of(B) =", align_of(B));
|
||||
}
|
||||
|
||||
{
|
||||
// Removal of ..< and ...
|
||||
for i in 0..16 {
|
||||
}
|
||||
// Is similar to
|
||||
for _i := 0; _i < 16; _i++ { immutable i := _i;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
#label thing
|
||||
for i in 0..10 {
|
||||
for j := i+1; j < 10; j++ {
|
||||
if j == 2 {
|
||||
fmt.println(i, j);
|
||||
break thing;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
cond := true;
|
||||
x: int;
|
||||
if cond {
|
||||
x = 3;
|
||||
} else {
|
||||
x = 4;
|
||||
}
|
||||
|
||||
|
||||
// Ternary operator
|
||||
y := cond ? 3 : 4;
|
||||
|
||||
FOO :: true ? 123 : 432; // Constant ternary operation
|
||||
fmt.println("Ternary values:", y, FOO);
|
||||
}
|
||||
|
||||
{
|
||||
// Slices now store a capacity
|
||||
buf: [256]byte;
|
||||
s: []byte;
|
||||
s = buf[..0]; // == buf[0..0];
|
||||
fmt.println("count =", s.count);
|
||||
fmt.println("capacity =", s.capacity);
|
||||
append(s, 1, 2, 3);
|
||||
fmt.println(s);
|
||||
|
||||
s = buf[1..2..3];
|
||||
fmt.println("count =", s.count);
|
||||
fmt.println("capacity =", s.capacity);
|
||||
fmt.println(s);
|
||||
|
||||
clear(s); // Sets count to zero
|
||||
s.count = 0; // Equivalent
|
||||
}
|
||||
|
||||
{
|
||||
Foo :: struct {
|
||||
x, y, z: f32,
|
||||
ok: bool,
|
||||
flags: u32,
|
||||
}
|
||||
foo_array: [256]Foo;
|
||||
foo_as_bytes: []byte = slice_to_bytes(foo_array[..]);
|
||||
// Useful for things like
|
||||
// os.write(handle, foo_as_bytes);
|
||||
|
||||
foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo));
|
||||
// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
|
||||
// And if so what would the syntax be?
|
||||
// slice_transmute([]Foo, foo_as_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
Vec3 :: [vector 3]f32;
|
||||
|
||||
x := Vec3{1, 2, 3};
|
||||
y := Vec3{4, 5, 6};
|
||||
fmt.println(x < y);
|
||||
fmt.println(x + y);
|
||||
fmt.println(x - y);
|
||||
fmt.println(x * y);
|
||||
fmt.println(x / y);
|
||||
|
||||
for i in x {
|
||||
fmt.println(i);
|
||||
}
|
||||
|
||||
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
|
||||
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
|
||||
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
|
||||
}
|
||||
|
||||
{
|
||||
// fmt.* changes
|
||||
// bprint* returns `int` (bytes written)
|
||||
// sprint* returns `string` (bytes written as a string)
|
||||
|
||||
data: [256]byte;
|
||||
str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!');
|
||||
fmt.println(str);
|
||||
|
||||
buf := data[..0];
|
||||
count := fmt.bprintf(^buf, "Hellope %d %s %c", 123, "others", '!');
|
||||
fmt.println(cast(string)buf[..count]);
|
||||
|
||||
// NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet
|
||||
}
|
||||
|
||||
{
|
||||
x: [dynamic]f64;
|
||||
reserve(x, 16);
|
||||
defer free(x); // `free` is overloaded for numerous types
|
||||
// Number literals can have underscores in them for readability
|
||||
append(x, 2_000_000.500_000, 3, 5, 7); // variadic append
|
||||
|
||||
for p, i in x {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.print(p);
|
||||
}
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
{
|
||||
// Dynamic array "literals"
|
||||
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
|
||||
defer free(x);
|
||||
fmt.println(x); // fmt.print* supports printing of dynamic types
|
||||
clear(x);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
{
|
||||
m: map[f32]int;
|
||||
reserve(m, 16);
|
||||
@@ -109,49 +215,79 @@ when false {
|
||||
c := m["c"];
|
||||
_, ok := m["c"];
|
||||
assert(ok && c == 7654);
|
||||
fmt.println(m);
|
||||
|
||||
delete(m, "c"); // deletes entry with key "c"
|
||||
_, found := m["c"];
|
||||
assert(!found);
|
||||
|
||||
fmt.println(m);
|
||||
clear(m);
|
||||
fmt.println(m);
|
||||
|
||||
// NOTE: Fixed size maps are planned but we have not yet implemented
|
||||
// them as we have had no need for them as of yet
|
||||
}
|
||||
|
||||
{
|
||||
x: [dynamic]f64;
|
||||
reserve(x, 16);
|
||||
defer free(x);
|
||||
append(x, 2_000_000.500_000, 3, 5, 7);
|
||||
Vector3 :: struct{x, y, z: f32};
|
||||
Quaternion :: struct{x, y, z, w: f32};
|
||||
|
||||
for p, i in x {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.print(p);
|
||||
}
|
||||
fmt.println();
|
||||
}
|
||||
Entity :: union {
|
||||
// Common Fields
|
||||
id: u64,
|
||||
name: string,
|
||||
using position: Vector3,
|
||||
orientation: Quaternion,
|
||||
flags: u32,
|
||||
|
||||
{
|
||||
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
|
||||
defer free(x);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
Vec3 :: [vector 3]f32;
|
||||
|
||||
x := Vec3{1, 2, 3};
|
||||
y := Vec3{4, 5, 6};
|
||||
fmt.println(x < y);
|
||||
fmt.println(x + y);
|
||||
fmt.println(x - y);
|
||||
fmt.println(x * y);
|
||||
fmt.println(x / y);
|
||||
|
||||
for i in x {
|
||||
fmt.println(i);
|
||||
// Variants
|
||||
Frog{
|
||||
ribbit_volume: f32,
|
||||
jump_height: f32,
|
||||
},
|
||||
Door{
|
||||
openness: f32,
|
||||
},
|
||||
Map{
|
||||
width, height: f32,
|
||||
place_positions: []Vector3,
|
||||
place_names: []string,
|
||||
},
|
||||
}
|
||||
|
||||
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
|
||||
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
|
||||
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
|
||||
entity: Entity;
|
||||
// implicit conversion from variant to base type
|
||||
entity = Entity.Frog{
|
||||
id = 1337,
|
||||
ribbit_volume = 0.5,
|
||||
jump_height = 2.1,
|
||||
/*other data */
|
||||
};
|
||||
|
||||
entity.name = "Frank";
|
||||
entity.position = Vector3{1, 4, 9};
|
||||
|
||||
using Entity;
|
||||
match e in entity {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Door:
|
||||
fmt.println("Creak");
|
||||
case Map:
|
||||
fmt.println("Rustle");
|
||||
default:
|
||||
fmt.println("Just a normal entity");
|
||||
}
|
||||
|
||||
if frog, ok := union_cast(Frog)entity; ok {
|
||||
fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
|
||||
}
|
||||
|
||||
// Panics if not the correct type
|
||||
frog: Frog;
|
||||
frog = union_cast(Frog)entity;
|
||||
frog, _ = union_cast(Frog)entity; // ignore error and force cast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
10
src/array.c
10
src/array.c
@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
{
|
||||
// TODO(bill): Resize rather than copy and delete
|
||||
void *new_data = gb_alloc(x->allocator, element_size*capacity);
|
||||
gb_memmove(new_data, x->e, element_size*x->count);
|
||||
gb_free(x->allocator, x->e);
|
||||
x->capacity = capacity;
|
||||
x->e = new_data;
|
||||
}
|
||||
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
|
||||
x->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
202
src/check_expr.c
202
src/check_expr.c
@@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
// TODO(bill): is this a good enough error message?
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
error_node(operand->expr,
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
@@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
|
||||
Entity **found = map_entity_get(&entity_map, key);
|
||||
if (found != NULL) {
|
||||
Entity *e = *found;
|
||||
// TODO(bill): Scope checking already checks the declaration
|
||||
// NOTE(bill): Scope checking already checks the declaration but in many cases, this can happen so why not?
|
||||
// This may be a little janky but it's not really that much of a problem
|
||||
error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
|
||||
error(e->token, "\tpreviously declared");
|
||||
} else {
|
||||
@@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
|
||||
{
|
||||
ast_node(fl, FieldList, f->list);
|
||||
|
||||
// TODO(bill): Just do a gb_memcopy here
|
||||
// NOTE(bill): Copy the contents for the common fields for now
|
||||
AstNodeArray list = {0};
|
||||
array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
|
||||
for (isize j = 0; j < ut->fields.count; j++) {
|
||||
list.e[j] = ut->fields.e[j];
|
||||
}
|
||||
for (isize j = 0; j < fl->list.count; j++) {
|
||||
list.e[j+ut->fields.count] = fl->list.e[j];
|
||||
}
|
||||
gb_memmove_array(list.e, ut->fields.e, ut->fields.count);
|
||||
gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count);
|
||||
|
||||
isize list_count = 0;
|
||||
for_array(j, list) {
|
||||
@@ -654,7 +651,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
|
||||
|
||||
HashKey key = hash_string(name_token.string);
|
||||
if (map_entity_get(&entity_map, key) != NULL) {
|
||||
// TODO(bill): Scope checking already checks the declaration
|
||||
// NOTE(bill): Scope checking already checks the declaration
|
||||
error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
|
||||
} else {
|
||||
map_entity_set(&entity_map, key, e);
|
||||
@@ -1142,10 +1139,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
}
|
||||
break;
|
||||
|
||||
case Entity_TypeName: {
|
||||
case Entity_TypeName:
|
||||
// NOTE(bill): Cyclical dependency checking is handled in the "type system" not here
|
||||
o->mode = Addressing_Type;
|
||||
// TODO(bill): Fix cyclical dependancy checker
|
||||
} break;
|
||||
break;
|
||||
|
||||
case Entity_Procedure:
|
||||
o->mode = Addressing_Value;
|
||||
@@ -1165,6 +1162,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
|
||||
return e;
|
||||
|
||||
case Entity_Label:
|
||||
o->mode = Addressing_NoValue;
|
||||
break;
|
||||
|
||||
case Entity_Nil:
|
||||
o->mode = Addressing_Value;
|
||||
break;
|
||||
@@ -1691,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
|
||||
if (s < 64) {
|
||||
umax = (1ull << s) - 1ull;
|
||||
} else {
|
||||
// TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
|
||||
// IMPORTANT TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
|
||||
s = 64;
|
||||
}
|
||||
i64 imax = (1ll << (s-1ll));
|
||||
@@ -2884,7 +2885,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
operand->value = entity->Constant.value;
|
||||
break;
|
||||
case Entity_Variable:
|
||||
// TODO(bill): This is the rule I need?
|
||||
// TODO(bill): Is this the rule I need?
|
||||
if (operand->mode == Addressing_Immutable) {
|
||||
// Okay
|
||||
} else if (sel.indirect || operand->mode != Addressing_Value) {
|
||||
@@ -3073,9 +3074,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
|
||||
case BuiltinProc_clear: {
|
||||
Type *type = operand->type;
|
||||
if (!is_type_dynamic_array(type) && !is_type_map(type)) {
|
||||
bool is_pointer = is_type_pointer(type);
|
||||
type = base_type(type_deref(type));
|
||||
if (!is_type_dynamic_array(type) && !is_type_map(type) && !is_type_slice(type)) {
|
||||
gbString str = type_to_string(type);
|
||||
error_node(operand->expr, "Expected a map or dynamic array, got `%s`", str);
|
||||
error_node(operand->expr, "Invalid type for `clear`, got `%s`", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
@@ -3107,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
}
|
||||
|
||||
Type *elem = NULL;
|
||||
Type *slice_elem = NULL;
|
||||
if (is_type_dynamic_array(type)) {
|
||||
// TODO(bill): Semi-memory leaks
|
||||
elem = type->DynamicArray.elem;
|
||||
} else {
|
||||
elem = type->Slice.elem;
|
||||
}
|
||||
slice_elem = make_type_slice(c->allocator, elem);
|
||||
Type *slice_elem = make_type_slice(c->allocator, elem);
|
||||
|
||||
Type *proc_type_params = make_type_tuple(c->allocator);
|
||||
proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2);
|
||||
@@ -3501,113 +3502,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
operand->mode = Addressing_Value;
|
||||
} break;
|
||||
|
||||
#if 0
|
||||
case BuiltinProc_ptr_offset: {
|
||||
// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
|
||||
// ^T cannot be rawptr
|
||||
Type *ptr_type = base_type(operand->type);
|
||||
if (!is_type_pointer(ptr_type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error_node(call,
|
||||
"Expected a pointer to `ptr_offset`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ptr_type == t_rawptr) {
|
||||
error_node(call,
|
||||
"`rawptr` cannot have pointer arithmetic");
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *offset = ce->args.e[1];
|
||||
Operand op = {0};
|
||||
check_expr(c, &op, offset);
|
||||
if (op.mode == Addressing_Invalid)
|
||||
return false;
|
||||
Type *offset_type = base_type(op.type);
|
||||
if (!is_type_integer(offset_type)) {
|
||||
error_node(op.expr, "Pointer offsets for `ptr_offset` must be an integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Constant &&
|
||||
op.mode == Addressing_Constant) {
|
||||
i64 ptr = operand->value.value_pointer;
|
||||
i64 elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
|
||||
ptr += elem_size * op.value.value_integer;
|
||||
operand->value.value_pointer = ptr;
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case BuiltinProc_ptr_sub: {
|
||||
// ptr_sub :: proc(a, b: ^T) -> int
|
||||
// ^T cannot be rawptr
|
||||
Type *ptr_type = base_type(operand->type);
|
||||
if (!is_type_pointer(ptr_type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error_node(call,
|
||||
"Expected a pointer to `ptr_add`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ptr_type == t_rawptr) {
|
||||
error_node(call,
|
||||
"`rawptr` cannot have pointer arithmetic");
|
||||
return false;
|
||||
}
|
||||
AstNode *offset = ce->args[1];
|
||||
Operand op = {0};
|
||||
check_expr(c, &op, offset);
|
||||
if (op.mode == Addressing_Invalid)
|
||||
return false;
|
||||
if (!is_type_pointer(op.type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error_node(call,
|
||||
"Expected a pointer to `ptr_add`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (base_type(op.type) == t_rawptr) {
|
||||
error_node(call,
|
||||
"`rawptr` cannot have pointer arithmetic");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!are_types_identical(operand->type, op.type)) {
|
||||
gbString a = type_to_string(operand->type);
|
||||
gbString b = type_to_string(op.type);
|
||||
defer (gb_string_free(a));
|
||||
defer (gb_string_free(b));
|
||||
error_node(op.expr,
|
||||
"`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->type = t_int;
|
||||
|
||||
if (operand->mode == Addressing_Constant &&
|
||||
op.mode == Addressing_Constant) {
|
||||
u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
|
||||
u8 *ptr_b = cast(u8 *)op.value.value_pointer;
|
||||
isize elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
|
||||
operand->value = exact_value_integer((ptr_a - ptr_b) / elem_size);
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
case BuiltinProc_slice_ptr: {
|
||||
// slice_ptr :: proc(a: ^T, len: int) -> []T
|
||||
// slice_ptr :: proc(a: ^T, len, cap: int) -> []T
|
||||
// ^T cannot be rawptr
|
||||
Type *ptr_type = base_type(operand->type);
|
||||
if (!is_type_pointer(ptr_type)) {
|
||||
@@ -3625,21 +3522,28 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *len = ce->args.e[1];
|
||||
isize arg_count = ce->args.count;
|
||||
if (arg_count < 2 || 3 < arg_count) {
|
||||
error_node(ce->args.e[0], "`slice_ptr` expects 2 or 3 arguments, found %td", arg_count);
|
||||
// NOTE(bill): Return the correct type to reduce errors
|
||||
} else {
|
||||
// If any are constant
|
||||
i64 sizes[2] = {0};
|
||||
isize size_count = 0;
|
||||
for (isize i = 1; i < arg_count; i++) {
|
||||
i64 val = 0;
|
||||
bool ok = check_index_value(c, ce->args.e[i], -1, &val);
|
||||
if (ok && val >= 0) {
|
||||
GB_ASSERT(size_count < gb_count_of(sizes));
|
||||
sizes[size_count++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
Operand op = {0};
|
||||
check_expr(c, &op, len);
|
||||
if (op.mode == Addressing_Invalid)
|
||||
return false;
|
||||
if (!is_type_integer(op.type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error_node(call,
|
||||
"Length for `slice_ptr` must be an integer, got `%s`",
|
||||
type_str);
|
||||
gb_string_free(type_str);
|
||||
return false;
|
||||
if (size_count == 2 && sizes[0] > sizes[1]) {
|
||||
error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped");
|
||||
// No need quit
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
|
||||
operand->mode = Addressing_Value;
|
||||
} break;
|
||||
@@ -4491,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
case_end;
|
||||
|
||||
case_ast_node(te, TernaryExpr, node);
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(node, "A ternary expression is only allowed within a procedure");
|
||||
goto error;
|
||||
}
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, te->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
Operand cond = {Addressing_Invalid};
|
||||
check_expr(c, &cond, te->cond);
|
||||
if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
|
||||
error_node(te->cond, "Non-boolean condition in if expression");
|
||||
}
|
||||
|
||||
@@ -4539,6 +4439,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
|
||||
o->type = x.type;
|
||||
o->mode = Addressing_Value;
|
||||
|
||||
if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
|
||||
x.mode == Addressing_Constant &&
|
||||
y.mode == Addressing_Constant) {
|
||||
|
||||
o->mode = Addressing_Constant;
|
||||
|
||||
if (cond.value.value_bool) {
|
||||
o->value = x.value;
|
||||
} else {
|
||||
o->value = y.value;
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
case_ast_node(cl, CompoundLit, node);
|
||||
|
||||
136
src/check_stmt.c
136
src/check_stmt.c
@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
|
||||
return rhs->type;
|
||||
}
|
||||
|
||||
bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
|
||||
if (is_type_pointer(type)) {
|
||||
*is_union_ptr = is_type_union(type_deref(type));
|
||||
return *is_union_ptr;
|
||||
typedef enum MatchTypeKind {
|
||||
MatchType_Invalid,
|
||||
MatchType_Union,
|
||||
MatchType_Any,
|
||||
} MatchTypeKind;
|
||||
|
||||
MatchTypeKind check_valid_type_match_type(Type *type) {
|
||||
type = type_deref(type);
|
||||
if (is_type_union(type)) {
|
||||
return MatchType_Union;
|
||||
}
|
||||
if (is_type_any(type)) {
|
||||
*is_any = true;
|
||||
return *is_any;
|
||||
return MatchType_Any;
|
||||
}
|
||||
return false;
|
||||
return MatchType_Invalid;
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
|
||||
@@ -385,6 +390,47 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_label(Checker *c, AstNode *label) {
|
||||
if (label == NULL) {
|
||||
return;
|
||||
}
|
||||
ast_node(l, Label, label);
|
||||
if (l->name->kind != AstNode_Ident) {
|
||||
error_node(l->name, "A label's name must be an identifier");
|
||||
return;
|
||||
}
|
||||
String name = l->name->Ident.string;
|
||||
if (str_eq(name, str_lit("_"))) {
|
||||
error_node(l->name, "A label's name cannot be a blank identifier");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(l->name, "A label is only allowed within a procedure");
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(c->context.decl != NULL);
|
||||
|
||||
bool ok = true;
|
||||
for_array(i, c->context.decl->labels) {
|
||||
BlockLabel bl = c->context.decl->labels.e[i];
|
||||
if (str_eq(bl.name, name)) {
|
||||
error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
|
||||
add_entity(c, c->context.scope, l->name, e);
|
||||
|
||||
if (ok) {
|
||||
BlockLabel bl = {name, label};
|
||||
array_add(&c->context.decl->labels, bl);
|
||||
}
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
|
||||
// leave it?
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||
@@ -515,7 +558,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
// TODO(bill): Check if valid assignment operator
|
||||
Operand operand = {Addressing_Invalid};
|
||||
AstNode binary_expr = {AstNode_BinaryExpr};
|
||||
ast_node(be, BinaryExpr, &binary_expr);
|
||||
@@ -580,7 +622,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
if (c->context.in_defer) {
|
||||
error(rs->token, "You cannot `return` within a defer statement");
|
||||
// TODO(bill): Should I break here?
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
if (fs->init != NULL) {
|
||||
check_stmt(c, fs->init, 0);
|
||||
@@ -648,6 +691,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
check_open_scope(c, node);
|
||||
|
||||
check_label(c, rs->label);
|
||||
|
||||
Type *val = NULL;
|
||||
Type *idx = NULL;
|
||||
Entity *entities[2] = {0};
|
||||
@@ -718,7 +763,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
case Token_Ellipsis: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid range operator"); break;
|
||||
}
|
||||
bool ok = compare_exact_values(Token_Lt, a, b);
|
||||
bool ok = compare_exact_values(op, a, b);
|
||||
if (!ok) {
|
||||
// TODO(bill): Better error message
|
||||
error(ie->op, "Invalid interval range");
|
||||
@@ -982,8 +1027,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
mod_flags |= Stmt_BreakAllowed;
|
||||
check_open_scope(c, node);
|
||||
|
||||
bool is_union_ptr = false;
|
||||
bool is_any = false;
|
||||
MatchTypeKind match_type_kind = MatchType_Invalid;
|
||||
|
||||
if (ms->tag->kind != AstNode_AssignStmt) {
|
||||
error_node(ms->tag, "Expected an `in` assignment for this type match statement");
|
||||
@@ -1005,7 +1049,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
check_expr(c, &x, rhs);
|
||||
check_assignment(c, &x, NULL, str_lit("type match expression"));
|
||||
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
|
||||
match_type_kind = check_valid_type_match_type(x.type);
|
||||
if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error_node(x.expr,
|
||||
"Invalid type for this type match expression, got `%s`", str);
|
||||
@@ -1062,14 +1107,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
// TODO(bill): Make robust
|
||||
Type *bt = base_type(type_deref(x.type));
|
||||
|
||||
|
||||
AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
|
||||
Type *case_type = NULL;
|
||||
if (type_expr != NULL) { // Otherwise it's a default expression
|
||||
Operand y = {0};
|
||||
check_expr_or_type(c, &y, type_expr);
|
||||
|
||||
if (is_union_ptr) {
|
||||
if (match_type_kind == MatchType_Union) {
|
||||
GB_ASSERT(is_type_union(bt));
|
||||
bool tag_type_found = false;
|
||||
for (isize i = 0; i < bt->Record.variant_count; i++) {
|
||||
@@ -1086,7 +1130,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
continue;
|
||||
}
|
||||
case_type = y.type;
|
||||
} else if (is_any) {
|
||||
} else if (match_type_kind == MatchType_Any) {
|
||||
case_type = y.type;
|
||||
} else {
|
||||
GB_PANIC("Unknown type to type match statement");
|
||||
@@ -1110,11 +1154,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
if (case_type == NULL) {
|
||||
if (is_union_ptr) {
|
||||
case_type = type_deref(x.type);
|
||||
} else {
|
||||
case_type = x.type;
|
||||
}
|
||||
case_type = type_deref(x.type);
|
||||
}
|
||||
|
||||
add_type_info_type(c, case_type);
|
||||
@@ -1122,7 +1162,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
{
|
||||
// NOTE(bill): Dummy type
|
||||
Type *tt = case_type;
|
||||
if (is_union_ptr) {
|
||||
if (is_type_pointer(x.type)) {
|
||||
tt = make_type_pointer(c->allocator, case_type);
|
||||
add_type_info_type(c, tt);
|
||||
}
|
||||
@@ -1173,20 +1213,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
|
||||
break;
|
||||
}
|
||||
|
||||
if (bs->label != NULL) {
|
||||
if (bs->label->kind != AstNode_Ident) {
|
||||
error_node(bs->label, "A branch statement's label name must be an identifier");
|
||||
return;
|
||||
}
|
||||
AstNode *ident = bs->label;
|
||||
String name = ident->Ident.string;
|
||||
Entity *e = scope_lookup_entity(c->context.scope, name);
|
||||
if (e == NULL) {
|
||||
error_node(ident, "Undeclared label name: %.*s", LIT(name));
|
||||
return;
|
||||
}
|
||||
add_entity_use(c, ident, e);
|
||||
if (e->kind != Entity_Label) {
|
||||
error_node(ident, "`%.*s` is not a label", LIT(name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
case_ast_node(us, UsingStmt, node);
|
||||
switch (us->node->kind) {
|
||||
default:
|
||||
// TODO(bill): Better error message for invalid using statement
|
||||
error(us->token, "Invalid `using` statement");
|
||||
break;
|
||||
case_ast_node(es, ExprStmt, us->node);
|
||||
// TODO(bill): Allow for just a LHS expression list rather than this silly code
|
||||
if (us->list.count == 0) {
|
||||
error(us->token, "Empty `using` list");
|
||||
return;
|
||||
}
|
||||
for_array(i, us->list) {
|
||||
AstNode *expr = unparen_expr(us->list.e[0]);
|
||||
Entity *e = NULL;
|
||||
|
||||
bool is_selector = false;
|
||||
AstNode *expr = unparen_expr(es->expr);
|
||||
if (expr->kind == AstNode_Ident) {
|
||||
String name = expr->Ident.string;
|
||||
e = scope_lookup_entity(c->context.scope, name);
|
||||
@@ -1194,11 +1252,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
Operand o = {0};
|
||||
e = check_selector(c, &o, expr, NULL);
|
||||
is_selector = true;
|
||||
} else if (expr->kind == AstNode_Implicit) {
|
||||
error(us->token, "`using` applied to an implicit value");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e == NULL) {
|
||||
error(us->token, "`using` applied to an unknown entity");
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (e->kind) {
|
||||
@@ -1296,6 +1357,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
error(us->token, "`using` cannot be applied to `nil`");
|
||||
break;
|
||||
|
||||
case Entity_Label:
|
||||
error(us->token, "`using` cannot be applied to a label");
|
||||
break;
|
||||
|
||||
case Entity_Invalid:
|
||||
error(us->token, "`using` cannot be applied to an invalid entity");
|
||||
break;
|
||||
@@ -1303,13 +1368,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
default:
|
||||
GB_PANIC("TODO(bill): `using` other expressions?");
|
||||
}
|
||||
case_end;
|
||||
|
||||
}
|
||||
case_end;
|
||||
|
||||
|
||||
|
||||
case_ast_node(pa, PushAllocator, node);
|
||||
Operand op = {0};
|
||||
check_expr(c, &op, pa->expr);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
16
src/entity.c
16
src/entity.c
@@ -15,12 +15,13 @@ typedef struct Type Type;
|
||||
ENTITY_KIND(LibraryName) \
|
||||
ENTITY_KIND(Alias) \
|
||||
ENTITY_KIND(Nil) \
|
||||
ENTITY_KIND(Count)
|
||||
ENTITY_KIND(Label)
|
||||
|
||||
typedef enum EntityKind {
|
||||
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
|
||||
ENTITY_KINDS
|
||||
#undef ENTITY_KIND
|
||||
Entity_Count,
|
||||
} EntityKind;
|
||||
|
||||
String const entity_strings[] = {
|
||||
@@ -100,6 +101,10 @@ struct Entity {
|
||||
Entity *original;
|
||||
} Alias;
|
||||
i32 Nil;
|
||||
struct {
|
||||
String name;
|
||||
AstNode *node;
|
||||
} Label;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -235,6 +240,15 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||
AstNode *node) {
|
||||
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
|
||||
entity->Label.node = node;
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||
token.string = str_lit("_");
|
||||
return make_entity_variable(a, scope, token, NULL, false);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
252
src/ir.c
@@ -91,7 +91,7 @@ typedef enum irDeferKind {
|
||||
|
||||
typedef struct irDefer {
|
||||
irDeferKind kind;
|
||||
isize scope_index;
|
||||
isize scope_index;
|
||||
irBlock * block;
|
||||
union {
|
||||
AstNode *stmt;
|
||||
@@ -100,32 +100,41 @@ typedef struct irDefer {
|
||||
};
|
||||
} irDefer;
|
||||
|
||||
|
||||
typedef struct irBranchBlocks {
|
||||
AstNode *label;
|
||||
irBlock *break_;
|
||||
irBlock *continue_;
|
||||
} irBranchBlocks;
|
||||
|
||||
|
||||
struct irProcedure {
|
||||
irProcedure * parent;
|
||||
Array(irProcedure *) children;
|
||||
irProcedure * parent;
|
||||
Array(irProcedure *) children;
|
||||
|
||||
Entity * entity;
|
||||
irModule * module;
|
||||
String name;
|
||||
Type * type;
|
||||
AstNode * type_expr;
|
||||
AstNode * body;
|
||||
u64 tags;
|
||||
Entity * entity;
|
||||
irModule * module;
|
||||
String name;
|
||||
Type * type;
|
||||
AstNode * type_expr;
|
||||
AstNode * body;
|
||||
u64 tags;
|
||||
|
||||
irValueArray params;
|
||||
Array(irDefer) defer_stmts;
|
||||
Array(irBlock *) blocks;
|
||||
i32 scope_index;
|
||||
irBlock * decl_block;
|
||||
irBlock * entry_block;
|
||||
irBlock * curr_block;
|
||||
irTargetList * target_list;
|
||||
irValueArray referrers;
|
||||
irValueArray params;
|
||||
Array(irDefer) defer_stmts;
|
||||
Array(irBlock *) blocks;
|
||||
i32 scope_index;
|
||||
irBlock * decl_block;
|
||||
irBlock * entry_block;
|
||||
irBlock * curr_block;
|
||||
irTargetList * target_list;
|
||||
irValueArray referrers;
|
||||
|
||||
Array(irBranchBlocks) branch_blocks;
|
||||
|
||||
i32 local_count;
|
||||
i32 instr_count;
|
||||
i32 block_count;
|
||||
i32 local_count;
|
||||
i32 instr_count;
|
||||
i32 block_count;
|
||||
};
|
||||
|
||||
#define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||
@@ -1328,10 +1337,10 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
|
||||
irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
|
||||
#if 1
|
||||
// NOTE(bill): Sanity check
|
||||
Type *a = core_type(type_deref(ir_type(address)));
|
||||
Type *b = core_type(ir_type(value));
|
||||
Type *a = type_deref(ir_type(address));
|
||||
Type *b = ir_type(value);
|
||||
if (!is_type_untyped(b)) {
|
||||
GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b));
|
||||
GB_ASSERT_MSG(are_types_identical(core_type(a), core_type(b)), "%s %s", type_to_string(a), type_to_string(b));
|
||||
}
|
||||
#endif
|
||||
return ir_emit(p, ir_instr_store(p, address, value));
|
||||
@@ -1801,8 +1810,8 @@ irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index) {
|
||||
|
||||
irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) {
|
||||
Type *t = ir_type(u);
|
||||
GB_ASSERT(is_type_pointer(t) &&
|
||||
is_type_union(type_deref(t)));
|
||||
GB_ASSERT_MSG(is_type_pointer(t) &&
|
||||
is_type_union(type_deref(t)), "%s", type_to_string(t));
|
||||
irValue *tag_ptr = ir_emit(proc, ir_instr_union_tag_ptr(proc, u));
|
||||
Type *tpt = ir_type(tag_ptr);
|
||||
GB_ASSERT(is_type_pointer(tpt));
|
||||
@@ -1948,8 +1957,9 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
|
||||
}
|
||||
|
||||
|
||||
irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selection sel) {
|
||||
irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) {
|
||||
GB_ASSERT(sel.index.count > 0);
|
||||
Type *type = type_deref(ir_type(e));
|
||||
|
||||
for_array(i, sel.index) {
|
||||
i32 index = cast(i32)sel.index.e[i];
|
||||
@@ -1958,7 +1968,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
|
||||
e = ir_emit_load(proc, e);
|
||||
e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
|
||||
}
|
||||
type = base_type(type);
|
||||
type = core_type(type);
|
||||
|
||||
|
||||
if (is_type_raw_union(type)) {
|
||||
@@ -2005,7 +2015,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
|
||||
case 2: e = ir_emit_struct_ep(proc, e, 3); break; // allocator
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("un-gep-able type");
|
||||
GB_PANIC("un-gep-able type %s", type_to_string(type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2013,8 +2023,9 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
|
||||
}
|
||||
|
||||
|
||||
irValue *ir_emit_deep_field_ev(irProcedure *proc, Type *type, irValue *e, Selection sel) {
|
||||
irValue *ir_emit_deep_field_ev(irProcedure *proc, irValue *e, Selection sel) {
|
||||
GB_ASSERT(sel.index.count > 0);
|
||||
Type *type = ir_type(e);
|
||||
|
||||
for_array(i, sel.index) {
|
||||
i32 index = cast(i32)sel.index.e[i];
|
||||
@@ -2359,7 +2370,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
||||
if (src_is_ptr) {
|
||||
value = ir_emit_load(proc, value);
|
||||
}
|
||||
return ir_emit_deep_field_ev(proc, sb, value, sel);
|
||||
return ir_emit_deep_field_ev(proc, value, sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3368,8 +3379,13 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
|
||||
|
||||
case BuiltinProc_clear: {
|
||||
ir_emit_comment(proc, str_lit("clear"));
|
||||
irValue *ptr = ir_build_addr(proc, ce->args.e[0]).addr;
|
||||
Type *t = base_type(type_deref(ir_type(ptr)));
|
||||
Type *original_type = type_of_expr(proc->module->info, ce->args.e[0]);
|
||||
irAddr addr = ir_build_addr(proc, ce->args.e[0]);
|
||||
irValue *ptr = addr.addr;
|
||||
if (is_type_pointer(original_type)) {
|
||||
ptr = ir_addr_load(proc, addr);
|
||||
}
|
||||
Type *t = base_type(type_deref(original_type));
|
||||
if (is_type_dynamic_array(t)) {
|
||||
irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1);
|
||||
ir_emit_store(proc, count_ptr, v_zero);
|
||||
@@ -3378,6 +3394,9 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
|
||||
irValue *ea = ir_emit_struct_ep(proc, ptr, 1);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, ha, 1), v_zero);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, ea, 1), v_zero);
|
||||
} else if (is_type_slice(t)) {
|
||||
irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1);
|
||||
ir_emit_store(proc, count_ptr, v_zero);
|
||||
} else {
|
||||
GB_PANIC("TODO(bill): ir clear for `%s`", type_to_string(t));
|
||||
}
|
||||
@@ -3627,10 +3646,15 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
|
||||
irValue *ptr = ir_build_expr(proc, ce->args.e[0]);
|
||||
irValue *count = ir_build_expr(proc, ce->args.e[1]);
|
||||
count = ir_emit_conv(proc, count, t_int);
|
||||
irValue *capacity = count;
|
||||
if (ce->args.count > 2) {
|
||||
capacity = ir_build_expr(proc, ce->args.e[2]);
|
||||
capacity = ir_emit_conv(proc, capacity, t_int);
|
||||
}
|
||||
|
||||
Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr)));
|
||||
irValue *slice = ir_add_local_generated(proc, slice_type);
|
||||
ir_fill_slice(proc, slice, ptr, count, count);
|
||||
ir_fill_slice(proc, slice, ptr, count, capacity);
|
||||
return ir_emit_load(proc, slice);
|
||||
} break;
|
||||
|
||||
@@ -3809,7 +3833,8 @@ irValue *ir_get_using_variable(irProcedure *proc, Entity *e) {
|
||||
v = ir_build_addr(proc, e->using_expr).addr;
|
||||
}
|
||||
GB_ASSERT(v != NULL);
|
||||
return ir_emit_deep_field_gep(proc, parent->type, v, sel);
|
||||
GB_ASSERT(parent->type == type_deref(ir_type(v)));
|
||||
return ir_emit_deep_field_gep(proc, v, sel);
|
||||
}
|
||||
|
||||
// irValue *ir_add_using_variable(irProcedure *proc, Entity *e) {
|
||||
@@ -3925,7 +3950,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
|
||||
GB_ASSERT(sel.entity != NULL);
|
||||
|
||||
irValue *a = ir_build_addr(proc, se->expr).addr;
|
||||
a = ir_emit_deep_field_gep(proc, type, a, sel);
|
||||
a = ir_emit_deep_field_gep(proc, a, sel);
|
||||
return ir_addr(a);
|
||||
} else {
|
||||
Type *type = base_type(type_of_expr(proc->module->info, se->expr));
|
||||
@@ -3937,7 +3962,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
|
||||
GB_ASSERT(sel.entity != NULL);
|
||||
|
||||
irValue *a = ir_build_addr(proc, se->expr).addr;
|
||||
a = ir_emit_deep_field_gep(proc, type, a, sel);
|
||||
a = ir_emit_deep_field_gep(proc, a, sel);
|
||||
return ir_addr(a);
|
||||
}
|
||||
case_end;
|
||||
@@ -4032,7 +4057,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
|
||||
if (using_field != NULL) {
|
||||
Selection sel = lookup_field(a, t, using_field->token.string, false);
|
||||
irValue *e = ir_build_addr(proc, ie->expr).addr;
|
||||
using_addr = ir_emit_deep_field_gep(proc, t, e, sel);
|
||||
using_addr = ir_emit_deep_field_gep(proc, e, sel);
|
||||
|
||||
t = using_field->type;
|
||||
}
|
||||
@@ -4817,6 +4842,44 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type
|
||||
if (done_) *done_ = done;
|
||||
}
|
||||
|
||||
void ir_set_label_blocks(irProcedure *proc, AstNode *label, irBlock *break_, irBlock *continue_) {
|
||||
if (label == NULL) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(label->kind == AstNode_Label);
|
||||
|
||||
|
||||
for_array(i, proc->branch_blocks) {
|
||||
irBranchBlocks *b = &proc->branch_blocks.e[i];
|
||||
GB_ASSERT(b->label != NULL && label != NULL);
|
||||
GB_ASSERT(b->label->kind == AstNode_Label);
|
||||
if (b->label == label) {
|
||||
b->break_ = break_;
|
||||
b->continue_ = continue_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("ir_set_label_blocks: Unreachable");
|
||||
}
|
||||
|
||||
irBranchBlocks ir_lookup_branch_blocks(irProcedure *proc, AstNode *ident) {
|
||||
GB_ASSERT(ident->kind == AstNode_Ident);
|
||||
Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(ident));
|
||||
GB_ASSERT(found != NULL);
|
||||
Entity *e = *found;
|
||||
GB_ASSERT(e->kind == Entity_Label);
|
||||
for_array(i, proc->branch_blocks) {
|
||||
irBranchBlocks *b = &proc->branch_blocks.e[i];
|
||||
if (b->label == e->Label.node) {
|
||||
return *b;
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Unreachable");
|
||||
return (irBranchBlocks){0};
|
||||
}
|
||||
|
||||
|
||||
void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
switch (node->kind) {
|
||||
@@ -4824,9 +4887,11 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(us, UsingStmt, node);
|
||||
AstNode *decl = unparen_expr(us->node);
|
||||
if (decl->kind == AstNode_ValueDecl) {
|
||||
ir_build_stmt(proc, decl);
|
||||
for_array(i, us->list) {
|
||||
AstNode *decl = unparen_expr(us->list.e[i]);
|
||||
if (decl->kind == AstNode_ValueDecl) {
|
||||
ir_build_stmt(proc, decl);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
@@ -4944,7 +5009,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
|
||||
|
||||
irValue *value = ir_value_procedure(proc->module->allocator,
|
||||
proc->module, e, e->type, pd->type, pd->body, name);
|
||||
proc->module, e, e->type, pd->type, pd->body, name);
|
||||
|
||||
value->Proc.tags = pd->tags;
|
||||
value->Proc.parent = proc;
|
||||
@@ -5172,6 +5237,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
ir_emit_comment(proc, str_lit("ForStmt"));
|
||||
|
||||
if (fs->init != NULL) {
|
||||
irBlock *init = ir_new_block(proc, node, "for.init");
|
||||
ir_emit_jump(proc, init);
|
||||
@@ -5188,6 +5254,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
if (fs->post != NULL) {
|
||||
post = ir_new_block(proc, node, "for.post");
|
||||
}
|
||||
|
||||
|
||||
ir_emit_jump(proc, loop);
|
||||
ir_start_block(proc, loop);
|
||||
|
||||
@@ -5196,6 +5264,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
ir_start_block(proc, body);
|
||||
}
|
||||
|
||||
ir_set_label_blocks(proc, fs->label, done, post);
|
||||
ir_push_target_list(proc, done, post, NULL);
|
||||
|
||||
ir_open_scope(proc);
|
||||
@@ -5330,6 +5399,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
ir_addr_store(proc, idx_addr, index);
|
||||
}
|
||||
|
||||
ir_set_label_blocks(proc, rs->label, done, loop);
|
||||
ir_push_target_list(proc, done, loop, NULL);
|
||||
|
||||
ir_open_scope(proc);
|
||||
@@ -5442,16 +5512,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
AstNode *rhs = as->rhs.e[0];
|
||||
|
||||
irValue *parent = ir_build_expr(proc, rhs);
|
||||
bool is_union_ptr = false;
|
||||
bool is_any = false;
|
||||
GB_ASSERT(check_valid_type_match_type(ir_type(parent), &is_union_ptr, &is_any));
|
||||
bool is_parent_ptr = is_type_pointer(ir_type(parent));
|
||||
MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent));
|
||||
GB_ASSERT(match_type_kind != MatchType_Invalid);
|
||||
|
||||
irValue *tag_index = NULL;
|
||||
irValue *union_data = NULL;
|
||||
if (is_union_ptr) {
|
||||
if (match_type_kind == MatchType_Union) {
|
||||
if (!is_parent_ptr) {
|
||||
parent = ir_address_from_load_or_generate_local(proc, parent);
|
||||
}
|
||||
ir_emit_comment(proc, str_lit("get union's tag"));
|
||||
tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent));
|
||||
union_data = ir_emit_conv(proc, parent, t_rawptr);
|
||||
} else if (match_type_kind == MatchType_Any) {
|
||||
if (!is_parent_ptr) {
|
||||
parent = ir_address_from_load_or_generate_local(proc, parent);
|
||||
}
|
||||
}
|
||||
|
||||
irBlock *start_block = ir_new_block(proc, node, "type-match.case.first");
|
||||
@@ -5478,7 +5555,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
Type *tag_var_type = NULL;
|
||||
if (str_eq(tag_var_name, str_lit("_"))) {
|
||||
Type *t = type_of_expr(proc->module->info, cc->list.e[0]);
|
||||
if (is_union_ptr) {
|
||||
if (match_type_kind == MatchType_Union) {
|
||||
t = make_type_pointer(proc->module->allocator, t);
|
||||
}
|
||||
tag_var_type = t;
|
||||
@@ -5505,7 +5582,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
} else {
|
||||
tag_var = ir_add_local_generated(proc, tag_var_type);
|
||||
}
|
||||
ir_emit_store(proc, tag_var, parent);
|
||||
|
||||
if (!is_parent_ptr) {
|
||||
ir_emit_store(proc, tag_var, ir_emit_load(proc, parent));
|
||||
} else {
|
||||
ir_emit_store(proc, tag_var, parent);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(cc->list.count == 1);
|
||||
@@ -5513,7 +5595,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
irBlock *body = ir_new_block(proc, clause, "type-match.case.body");
|
||||
|
||||
|
||||
if (is_union_ptr) {
|
||||
if (match_type_kind == MatchType_Union) {
|
||||
Type *bt = type_deref(tag_var_type);
|
||||
irValue *index = NULL;
|
||||
Type *ut = base_type(type_deref(ir_type(parent)));
|
||||
@@ -5534,20 +5616,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
tag_var = ir_add_local_generated(proc, tag_var_type);
|
||||
}
|
||||
|
||||
|
||||
irValue *data_ptr = ir_emit_conv(proc, union_data, tag_var_type);
|
||||
Type *bt_ptr = make_type_pointer(proc->module->allocator, bt);
|
||||
irValue *data_ptr = ir_emit_conv(proc, union_data, bt_ptr);
|
||||
if (!is_type_pointer(type_deref(ir_type(tag_var)))) {
|
||||
data_ptr = ir_emit_load(proc, data_ptr);
|
||||
}
|
||||
ir_emit_store(proc, tag_var, data_ptr);
|
||||
|
||||
cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index);
|
||||
} else if (is_any) {
|
||||
} else if (match_type_kind == MatchType_Any) {
|
||||
Type *type = tag_var_type;
|
||||
irValue *any_data = ir_emit_struct_ev(proc, parent, 1);
|
||||
irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 1));
|
||||
irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type));
|
||||
if (tag_var_entity != NULL) {
|
||||
ir_module_add_value(proc->module, tag_var_entity, data);
|
||||
}
|
||||
|
||||
irValue *any_ti = ir_emit_struct_ev(proc, parent, 0);
|
||||
irValue *any_ti = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 0));
|
||||
irValue *case_ti = ir_type_info(proc, type);
|
||||
cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
|
||||
} else {
|
||||
@@ -5588,22 +5673,34 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
|
||||
case_ast_node(bs, BranchStmt, node);
|
||||
irBlock *block = NULL;
|
||||
switch (bs->token.kind) {
|
||||
case Token_break:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->break_;
|
||||
|
||||
if (bs->label != NULL) {
|
||||
irBranchBlocks bb = ir_lookup_branch_blocks(proc, bs->label);
|
||||
switch (bs->token.kind) {
|
||||
case Token_break: block = bb.break_; break;
|
||||
case Token_continue: block = bb.continue_; break;
|
||||
case Token_fallthrough:
|
||||
GB_PANIC("fallthrough cannot have a label");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Token_continue:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->continue_;
|
||||
} else {
|
||||
switch (bs->token.kind) {
|
||||
case Token_break:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->break_;
|
||||
}
|
||||
break;
|
||||
case Token_continue:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->continue_;
|
||||
}
|
||||
break;
|
||||
case Token_fallthrough:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->fallthrough_;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Token_fallthrough:
|
||||
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
|
||||
block = t->fallthrough_;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (block != NULL) {
|
||||
ir_emit_defer_stmts(proc, irDeferExit_Branch, block);
|
||||
@@ -5692,9 +5789,20 @@ void ir_number_proc_registers(irProcedure *proc) {
|
||||
void ir_begin_procedure_body(irProcedure *proc) {
|
||||
array_add(&proc->module->procs, proc);
|
||||
|
||||
array_init(&proc->blocks, heap_allocator());
|
||||
array_init(&proc->defer_stmts, heap_allocator());
|
||||
array_init(&proc->children, heap_allocator());
|
||||
array_init(&proc->blocks, heap_allocator());
|
||||
array_init(&proc->defer_stmts, heap_allocator());
|
||||
array_init(&proc->children, heap_allocator());
|
||||
array_init(&proc->branch_blocks, heap_allocator());
|
||||
|
||||
DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity));
|
||||
if (found != NULL) {
|
||||
DeclInfo *decl = *found;
|
||||
for_array(i, decl->labels) {
|
||||
BlockLabel bl = decl->labels.e[i];
|
||||
irBranchBlocks bb = {bl.label, NULL, NULL};
|
||||
array_add(&proc->branch_blocks, bb);
|
||||
}
|
||||
}
|
||||
|
||||
proc->decl_block = ir_new_block(proc, proc->type_expr, "decls");
|
||||
ir_start_block(proc, proc->decl_block);
|
||||
|
||||
150
src/parser.c
150
src/parser.c
@@ -219,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(ForStmt, "for statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *post; \
|
||||
@@ -226,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(RangeStmt, "range statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *value; \
|
||||
AstNode *index; \
|
||||
Token in_token; \
|
||||
@@ -249,10 +251,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
AstNode *node; \
|
||||
AstNodeArray list; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
@@ -305,6 +307,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
AstNode *cond; \
|
||||
bool is_system; \
|
||||
}) \
|
||||
AST_NODE_KIND(Label, "label", struct { \
|
||||
Token token; \
|
||||
AstNode *name; \
|
||||
}) \
|
||||
AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
AstNodeArray names; \
|
||||
@@ -499,6 +505,7 @@ Token ast_node_token(AstNode *node) {
|
||||
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]);
|
||||
case AstNode_ImportDecl: return node->ImportDecl.token;
|
||||
case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
|
||||
case AstNode_Label: return node->Label.token;
|
||||
|
||||
|
||||
case AstNode_Field:
|
||||
@@ -869,9 +876,10 @@ AstNode *ast_return_stmt(AstFile *f, Token token, AstNodeArray results) {
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
|
||||
AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *label, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
|
||||
AstNode *result = make_ast_node(f, AstNode_ForStmt);
|
||||
result->ForStmt.token = token;
|
||||
result->ForStmt.label = label;
|
||||
result->ForStmt.init = init;
|
||||
result->ForStmt.cond = cond;
|
||||
result->ForStmt.post = post;
|
||||
@@ -879,8 +887,9 @@ AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
|
||||
AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *label, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
|
||||
AstNode *result = make_ast_node(f, AstNode_RangeStmt);
|
||||
result->RangeStmt.label = label;
|
||||
result->RangeStmt.token = token;
|
||||
result->RangeStmt.value = value;
|
||||
result->RangeStmt.index = index;
|
||||
@@ -924,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_branch_stmt(AstFile *f, Token token) {
|
||||
AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
|
||||
AstNode *result = make_ast_node(f, AstNode_BranchStmt);
|
||||
result->BranchStmt.token = token;
|
||||
result->BranchStmt.label = label;
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) {
|
||||
AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
|
||||
AstNode *result = make_ast_node(f, AstNode_UsingStmt);
|
||||
result->UsingStmt.token = token;
|
||||
result->UsingStmt.node = node;
|
||||
result->UsingStmt.list = list;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
|
||||
AstNode *result = make_ast_node(f, AstNode_AsmOperand);
|
||||
result->AsmOperand.string = string;
|
||||
@@ -1138,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
|
||||
AstNode *result = make_ast_node(f, AstNode_Label);
|
||||
result->Label.token = token;
|
||||
result->Label.name = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool next_token(AstFile *f) {
|
||||
Token prev = f->curr_token;
|
||||
@@ -2335,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
|
||||
return parse_body(f);
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow);
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
|
||||
|
||||
|
||||
AstNode *parse_results(AstFile *f) {
|
||||
@@ -2354,7 +2372,7 @@ AstNode *parse_results(AstFile *f) {
|
||||
|
||||
AstNode *list = NULL;
|
||||
expect_token(f, Token_OpenParen);
|
||||
list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen);
|
||||
list = parse_field_list(f, NULL, 0, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
return list;
|
||||
}
|
||||
@@ -2365,7 +2383,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
|
||||
|
||||
Token proc_token = expect_token(f, Token_proc);
|
||||
expect_token(f, Token_OpenParen);
|
||||
params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
|
||||
params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
results = parse_results(f);
|
||||
|
||||
@@ -2384,17 +2402,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
|
||||
return ast_proc_type(f, proc_token, params, results, tags, cc);
|
||||
}
|
||||
|
||||
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
|
||||
if (separator == Token_Semicolon) {
|
||||
expect_semicolon(f, param);
|
||||
} else {
|
||||
if (!allow_token(f, separator)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
|
||||
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
|
||||
Token tok = f->curr_token;
|
||||
@@ -2505,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
|
||||
return idents;
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
|
||||
|
||||
bool parse_expect_field_separator(AstFile *f, AstNode *param) {
|
||||
Token token = f->curr_token;
|
||||
if (allow_token(f, Token_Comma)) {
|
||||
return true;
|
||||
}
|
||||
if (token.kind == Token_Semicolon) {
|
||||
next_token(f);
|
||||
error(f->curr_token, "Expected a comma, got a semicolon");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
|
||||
TokenKind separator = Token_Comma;
|
||||
Token start_token = f->curr_token;
|
||||
|
||||
AstNodeArray params = make_ast_node_array(f);
|
||||
@@ -2543,7 +2565,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
AstNode *param = ast_field(f, names, type, set_flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
parse_expect_separator(f, separator, type);
|
||||
parse_expect_field_separator(f, type);
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
@@ -2561,7 +2583,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
AstNode *param = ast_field(f, names, type, set_flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (parse_expect_separator(f, separator, param)) {
|
||||
if (!parse_expect_field_separator(f, param)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2590,7 +2612,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
|
||||
|
||||
AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
|
||||
return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
|
||||
return parse_field_list(f, field_count_, flags, Token_CloseBrace);
|
||||
}
|
||||
|
||||
AstNode *parse_type_or_ident(AstFile *f) {
|
||||
@@ -2987,7 +3009,7 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
// return ast_expr_stmt(f, ge);
|
||||
// }
|
||||
|
||||
AstNode *parse_for_stmt(AstFile *f) {
|
||||
AstNode *parse_for_stmt(AstFile *f, AstNode *label) {
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(f->curr_token, "You cannot use a for statement in the file scope");
|
||||
return ast_bad_stmt(f, f->curr_token, f->curr_token);
|
||||
@@ -3051,11 +3073,11 @@ AstNode *parse_for_stmt(AstFile *f) {
|
||||
if (cond->AssignStmt.rhs.count > 0) {
|
||||
rhs = cond->AssignStmt.rhs.e[0];
|
||||
}
|
||||
return ast_range_stmt(f, token, value, index, in_token, rhs, body);
|
||||
return ast_range_stmt(f, token, label, value, index, in_token, rhs, body);
|
||||
}
|
||||
|
||||
cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
|
||||
return ast_for_stmt(f, token, init, cond, post, body);
|
||||
return ast_for_stmt(f, token, label, init, cond, post, body);
|
||||
|
||||
#if 0
|
||||
Token token = expect_token(f, Token_for);
|
||||
@@ -3262,7 +3284,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
|
||||
case Token_if: return parse_if_stmt(f);
|
||||
case Token_when: return parse_when_stmt(f);
|
||||
case Token_for: return parse_for_stmt(f);
|
||||
case Token_for: return parse_for_stmt(f, NULL);
|
||||
case Token_match: return parse_match_stmt(f);
|
||||
case Token_defer: return parse_defer_stmt(f);
|
||||
case Token_asm: return parse_asm_stmt(f);
|
||||
@@ -3271,43 +3293,47 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
|
||||
case Token_break:
|
||||
case Token_continue:
|
||||
case Token_fallthrough:
|
||||
case Token_fallthrough: {
|
||||
AstNode *label = NULL;
|
||||
next_token(f);
|
||||
s = ast_branch_stmt(f, token);
|
||||
if (token.kind != Token_fallthrough &&
|
||||
f->curr_token.kind == Token_Ident) {
|
||||
label = parse_ident(f);
|
||||
}
|
||||
s = ast_branch_stmt(f, token, label);
|
||||
expect_semicolon(f, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
case Token_using: {
|
||||
// TODO(bill): Make using statements better
|
||||
Token token = expect_token(f, Token_using);
|
||||
AstNode *node = parse_stmt(f);
|
||||
AstNodeArray list = parse_lhs_expr_list(f);
|
||||
if (list.count == 0) {
|
||||
syntax_error(token, "Illegal use of `using` statement");
|
||||
expect_semicolon(f, NULL);
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
switch (node->kind) {
|
||||
case AstNode_ValueDecl:
|
||||
if (!node->ValueDecl.is_var) {
|
||||
if (f->curr_token.kind != Token_Colon) {
|
||||
expect_semicolon(f, list.e[list.count-1]);
|
||||
return ast_using_stmt(f, token, list);
|
||||
}
|
||||
|
||||
AstNode *decl = parse_simple_stmt(f, false);
|
||||
expect_semicolon(f, decl);
|
||||
|
||||
if (decl->kind == AstNode_ValueDecl) {
|
||||
if (!decl->ValueDecl.is_var) {
|
||||
syntax_error(token, "`using` may not be applied to constant declarations");
|
||||
return decl;
|
||||
}
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(token, "`using` is not allowed at the file scope");
|
||||
} else {
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(token, "`using` is not allowed at the file scope");
|
||||
} else {
|
||||
node->ValueDecl.flags |= VarDeclFlag_using;
|
||||
}
|
||||
decl->ValueDecl.flags |= VarDeclFlag_using;
|
||||
}
|
||||
return node;
|
||||
case AstNode_ExprStmt: {
|
||||
AstNode *e = unparen_expr(node->ExprStmt.expr);
|
||||
while (e->kind == AstNode_SelectorExpr) {
|
||||
e = unparen_expr(e->SelectorExpr.selector);
|
||||
}
|
||||
if (e->kind == AstNode_Ident) {
|
||||
return ast_using_stmt(f, token, node);
|
||||
} else if (e->kind == AstNode_Implicit) {
|
||||
syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string));
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
syntax_error(token, "Illegal use of `using` statement");
|
||||
@@ -3518,6 +3544,20 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
|
||||
}
|
||||
return s;
|
||||
} else if (str_eq(tag, str_lit("label"))) {
|
||||
AstNode *name = parse_ident(f);
|
||||
AstNode *label = ast_label_decl(f, token, name);
|
||||
|
||||
Token tok = f->curr_token;
|
||||
switch (tok.kind) {
|
||||
case Token_for:
|
||||
return parse_for_stmt(f, label);
|
||||
default:
|
||||
syntax_error(token, "#label may only be applied to a loop");
|
||||
fix_advance_to_next_stmt(f);
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
277
src/ssa_op.c
Normal file
277
src/ssa_op.c
Normal 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
|
||||
};
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user