aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2017-03-19 16:59:11 +0000
committerGinger Bill <bill@gingerbill.org>2017-03-19 16:59:11 +0000
commit5562364a98f01a0c0221a70345656d45de0d2009 (patch)
tree3ea4409ec3fcd1b7469c96d0e6ff03b437f8f823 /src
parent32150e401e39bd68f9882c3983829e744603dac1 (diff)
Add branch labels for loops; using list
Diffstat (limited to 'src')
-rw-r--r--src/array.c10
-rw-r--r--src/check_decl.c7
-rw-r--r--src/check_expr.c202
-rw-r--r--src/check_stmt.c136
-rw-r--r--src/checker.c26
-rw-r--r--src/entity.c16
-rw-r--r--src/exact_value.c36
-rw-r--r--src/gb/gb.h4
-rw-r--r--src/ir.c260
-rw-r--r--src/parser.c150
-rw-r--r--src/ssa.c1030
-rw-r--r--src/ssa_op.c277
-rw-r--r--src/tokenizer.c36
13 files changed, 1424 insertions, 766 deletions
diff --git a/src/array.c b/src/array.c
index d169a99ef..bb9e789db 100644
--- a/src/array.c
+++ b/src/array.c
@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
x->count = capacity;
}
- {
- // TODO(bill): Resize rather than copy and delete
- void *new_data = gb_alloc(x->allocator, element_size*capacity);
- gb_memmove(new_data, x->e, element_size*x->count);
- gb_free(x->allocator, x->e);
- x->capacity = capacity;
- x->e = new_data;
- }
+ x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
+ x->capacity = capacity;
}
diff --git a/src/check_decl.c b/src/check_decl.c
index a89460123..12ce52142 100644
--- a/src/check_decl.c
+++ b/src/check_decl.c
@@ -12,6 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message?
+ // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
@@ -276,12 +277,6 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
}
- // TODO(bill): Is this the best option? What about passing to external shit?!
- // if (proc_type->Proc.calling_convention != ProcCC_Odin) {
- // error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
- // proc_type->Proc.calling_convention = ProcCC_Odin;
- // }
-
d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
diff --git a/src/check_expr.c b/src/check_expr.c
index c6c9e738b..fcb3e948e 100644
--- a/src/check_expr.c
+++ b/src/check_expr.c
@@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
if (operand->mode == Addressing_Builtin) {
// TODO(bill): is this a good enough error message?
+ // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
@@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
Entity **found = map_entity_get(&entity_map, key);
if (found != NULL) {
Entity *e = *found;
- // TODO(bill): Scope checking already checks the declaration
+ // NOTE(bill): Scope checking already checks the declaration but in many cases, this can happen so why not?
+ // This may be a little janky but it's not really that much of a problem
error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
error(e->token, "\tpreviously declared");
} else {
@@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
{
ast_node(fl, FieldList, f->list);
- // TODO(bill): Just do a gb_memcopy here
// NOTE(bill): Copy the contents for the common fields for now
AstNodeArray list = {0};
array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
- for (isize j = 0; j < ut->fields.count; j++) {
- list.e[j] = ut->fields.e[j];
- }
- for (isize j = 0; j < fl->list.count; j++) {
- list.e[j+ut->fields.count] = fl->list.e[j];
- }
+ gb_memmove_array(list.e, ut->fields.e, ut->fields.count);
+ gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count);
isize list_count = 0;
for_array(j, list) {
@@ -654,7 +651,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
HashKey key = hash_string(name_token.string);
if (map_entity_get(&entity_map, key) != NULL) {
- // TODO(bill): Scope checking already checks the declaration
+ // NOTE(bill): Scope checking already checks the declaration
error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
} else {
map_entity_set(&entity_map, key, e);
@@ -1142,10 +1139,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
}
break;
- case Entity_TypeName: {
+ case Entity_TypeName:
+ // NOTE(bill): Cyclical dependency checking is handled in the "type system" not here
o->mode = Addressing_Type;
- // TODO(bill): Fix cyclical dependancy checker
- } break;
+ break;
case Entity_Procedure:
o->mode = Addressing_Value;
@@ -1165,6 +1162,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
return e;
+ case Entity_Label:
+ o->mode = Addressing_NoValue;
+ break;
+
case Entity_Nil:
o->mode = Addressing_Value;
break;
@@ -1691,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
if (s < 64) {
umax = (1ull << s) - 1ull;
} else {
- // TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
+ // IMPORTANT TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
s = 64;
}
i64 imax = (1ll << (s-1ll));
@@ -2884,7 +2885,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
operand->value = entity->Constant.value;
break;
case Entity_Variable:
- // TODO(bill): This is the rule I need?
+ // TODO(bill): Is this the rule I need?
if (operand->mode == Addressing_Immutable) {
// Okay
} else if (sel.indirect || operand->mode != Addressing_Value) {
@@ -3073,9 +3074,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
case BuiltinProc_clear: {
Type *type = operand->type;
- if (!is_type_dynamic_array(type) && !is_type_map(type)) {
+ bool is_pointer = is_type_pointer(type);
+ type = base_type(type_deref(type));
+ if (!is_type_dynamic_array(type) && !is_type_map(type) && !is_type_slice(type)) {
gbString str = type_to_string(type);
- error_node(operand->expr, "Expected a map or dynamic array, got `%s`", str);
+ error_node(operand->expr, "Invalid type for `clear`, got `%s`", str);
gb_string_free(str);
return false;
}
@@ -3107,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
}
Type *elem = NULL;
- Type *slice_elem = NULL;
if (is_type_dynamic_array(type)) {
- // TODO(bill): Semi-memory leaks
elem = type->DynamicArray.elem;
} else {
elem = type->Slice.elem;
}
- slice_elem = make_type_slice(c->allocator, elem);
+ Type *slice_elem = make_type_slice(c->allocator, elem);
Type *proc_type_params = make_type_tuple(c->allocator);
proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2);
@@ -3501,113 +3502,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
operand->mode = Addressing_Value;
} break;
-#if 0
- case BuiltinProc_ptr_offset: {
- // ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
- // ^T cannot be rawptr
- Type *ptr_type = base_type(operand->type);
- if (!is_type_pointer(ptr_type)) {
- gbString type_str = type_to_string(operand->type);
- defer (gb_string_free(type_str));
- error_node(call,
- "Expected a pointer to `ptr_offset`, got `%s`",
- type_str);
- return false;
- }
-
- if (ptr_type == t_rawptr) {
- error_node(call,
- "`rawptr` cannot have pointer arithmetic");
- return false;
- }
-
- AstNode *offset = ce->args.e[1];
- Operand op = {0};
- check_expr(c, &op, offset);
- if (op.mode == Addressing_Invalid)
- return false;
- Type *offset_type = base_type(op.type);
- if (!is_type_integer(offset_type)) {
- error_node(op.expr, "Pointer offsets for `ptr_offset` must be an integer");
- return false;
- }
-
- if (operand->mode == Addressing_Constant &&
- op.mode == Addressing_Constant) {
- i64 ptr = operand->value.value_pointer;
- i64 elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
- ptr += elem_size * op.value.value_integer;
- operand->value.value_pointer = ptr;
- } else {
- operand->mode = Addressing_Value;
- }
-
- } break;
-
- case BuiltinProc_ptr_sub: {
- // ptr_sub :: proc(a, b: ^T) -> int
- // ^T cannot be rawptr
- Type *ptr_type = base_type(operand->type);
- if (!is_type_pointer(ptr_type)) {
- gbString type_str = type_to_string(operand->type);
- defer (gb_string_free(type_str));
- error_node(call,
- "Expected a pointer to `ptr_add`, got `%s`",
- type_str);
- return false;
- }
-
- if (ptr_type == t_rawptr) {
- error_node(call,
- "`rawptr` cannot have pointer arithmetic");
- return false;
- }
- AstNode *offset = ce->args[1];
- Operand op = {0};
- check_expr(c, &op, offset);
- if (op.mode == Addressing_Invalid)
- return false;
- if (!is_type_pointer(op.type)) {
- gbString type_str = type_to_string(operand->type);
- defer (gb_string_free(type_str));
- error_node(call,
- "Expected a pointer to `ptr_add`, got `%s`",
- type_str);
- return false;
- }
-
- if (base_type(op.type) == t_rawptr) {
- error_node(call,
- "`rawptr` cannot have pointer arithmetic");
- return false;
- }
-
- if (!are_types_identical(operand->type, op.type)) {
- gbString a = type_to_string(operand->type);
- gbString b = type_to_string(op.type);
- defer (gb_string_free(a));
- defer (gb_string_free(b));
- error_node(op.expr,
- "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
- return false;
- }
-
- operand->type = t_int;
-
- if (operand->mode == Addressing_Constant &&
- op.mode == Addressing_Constant) {
- u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
- u8 *ptr_b = cast(u8 *)op.value.value_pointer;
- isize elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
- operand->value = exact_value_integer((ptr_a - ptr_b) / elem_size);
- } else {
- operand->mode = Addressing_Value;
- }
- } break;
-#endif
-
case BuiltinProc_slice_ptr: {
// slice_ptr :: proc(a: ^T, len: int) -> []T
+ // slice_ptr :: proc(a: ^T, len, cap: int) -> []T
// ^T cannot be rawptr
Type *ptr_type = base_type(operand->type);
if (!is_type_pointer(ptr_type)) {
@@ -3625,21 +3522,28 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
return false;
}
- AstNode *len = ce->args.e[1];
+ isize arg_count = ce->args.count;
+ if (arg_count < 2 || 3 < arg_count) {
+ error_node(ce->args.e[0], "`slice_ptr` expects 2 or 3 arguments, found %td", arg_count);
+ // NOTE(bill): Return the correct type to reduce errors
+ } else {
+ // If any are constant
+ i64 sizes[2] = {0};
+ isize size_count = 0;
+ for (isize i = 1; i < arg_count; i++) {
+ i64 val = 0;
+ bool ok = check_index_value(c, ce->args.e[i], -1, &val);
+ if (ok && val >= 0) {
+ GB_ASSERT(size_count < gb_count_of(sizes));
+ sizes[size_count++] = val;
+ }
+ }
- Operand op = {0};
- check_expr(c, &op, len);
- if (op.mode == Addressing_Invalid)
- return false;
- if (!is_type_integer(op.type)) {
- gbString type_str = type_to_string(operand->type);
- error_node(call,
- "Length for `slice_ptr` must be an integer, got `%s`",
- type_str);
- gb_string_free(type_str);
- return false;
+ if (size_count == 2 && sizes[0] > sizes[1]) {
+ error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped");
+ // No need quit
+ }
}
-
operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
operand->mode = Addressing_Value;
} break;
@@ -4491,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case_end;
case_ast_node(te, TernaryExpr, node);
- if (c->proc_stack.count == 0) {
- error_node(node, "A ternary expression is only allowed within a procedure");
- goto error;
- }
- Operand operand = {Addressing_Invalid};
- check_expr(c, &operand, te->cond);
- if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+ Operand cond = {Addressing_Invalid};
+ check_expr(c, &cond, te->cond);
+ if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
error_node(te->cond, "Non-boolean condition in if expression");
}
@@ -4539,6 +4439,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
o->type = x.type;
o->mode = Addressing_Value;
+
+ if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
+ x.mode == Addressing_Constant &&
+ y.mode == Addressing_Constant) {
+
+ o->mode = Addressing_Constant;
+
+ if (cond.value.value_bool) {
+ o->value = x.value;
+ } else {
+ o->value = y.value;
+ }
+ }
+
case_end;
case_ast_node(cl, CompoundLit, node);
diff --git a/src/check_stmt.c b/src/check_stmt.c
index 1837dd404..5e8766f11 100644
--- a/src/check_stmt.c
+++ b/src/check_stmt.c
@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
return rhs->type;
}
-bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
- if (is_type_pointer(type)) {
- *is_union_ptr = is_type_union(type_deref(type));
- return *is_union_ptr;
+typedef enum MatchTypeKind {
+ MatchType_Invalid,
+ MatchType_Union,
+ MatchType_Any,
+} MatchTypeKind;
+
+MatchTypeKind check_valid_type_match_type(Type *type) {
+ type = type_deref(type);
+ if (is_type_union(type)) {
+ return MatchType_Union;
}
if (is_type_any(type)) {
- *is_any = true;
- return *is_any;
+ return MatchType_Any;
}
- return false;
+ return MatchType_Invalid;
}
void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
@@ -385,6 +390,47 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
}
}
+void check_label(Checker *c, AstNode *label) {
+ if (label == NULL) {
+ return;
+ }
+ ast_node(l, Label, label);
+ if (l->name->kind != AstNode_Ident) {
+ error_node(l->name, "A label's name must be an identifier");
+ return;
+ }
+ String name = l->name->Ident.string;
+ if (str_eq(name, str_lit("_"))) {
+ error_node(l->name, "A label's name cannot be a blank identifier");
+ return;
+ }
+
+
+ if (c->proc_stack.count == 0) {
+ error_node(l->name, "A label is only allowed within a procedure");
+ return;
+ }
+ GB_ASSERT(c->context.decl != NULL);
+
+ bool ok = true;
+ for_array(i, c->context.decl->labels) {
+ BlockLabel bl = c->context.decl->labels.e[i];
+ if (str_eq(bl.name, name)) {
+ error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
+ ok = false;
+ break;
+ }
+ }
+
+ Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
+ add_entity(c, c->context.scope, l->name, e);
+
+ if (ok) {
+ BlockLabel bl = {name, label};
+ array_add(&c->context.decl->labels, bl);
+ }
+}
+
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return;
}
- // TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
- // leave it?
-
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
@@ -515,7 +558,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
return;
}
- // TODO(bill): Check if valid assignment operator
Operand operand = {Addressing_Invalid};
AstNode binary_expr = {AstNode_BinaryExpr};
ast_node(be, BinaryExpr, &binary_expr);
@@ -580,7 +622,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (c->context.in_defer) {
error(rs->token, "You cannot `return` within a defer statement");
- // TODO(bill): Should I break here?
break;
}
@@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(fs, ForStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
+
check_open_scope(c, node);
+ check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
if (fs->init != NULL) {
check_stmt(c, fs->init, 0);
@@ -648,6 +691,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node);
+ check_label(c, rs->label);
+
Type *val = NULL;
Type *idx = NULL;
Entity *entities[2] = {0};
@@ -718,7 +763,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case Token_Ellipsis: op = Token_Lt; break;
default: error(ie->op, "Invalid range operator"); break;
}
- bool ok = compare_exact_values(Token_Lt, a, b);
+ bool ok = compare_exact_values(op, a, b);
if (!ok) {
// TODO(bill): Better error message
error(ie->op, "Invalid interval range");
@@ -982,8 +1027,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
mod_flags |= Stmt_BreakAllowed;
check_open_scope(c, node);
- bool is_union_ptr = false;
- bool is_any = false;
+ MatchTypeKind match_type_kind = MatchType_Invalid;
if (ms->tag->kind != AstNode_AssignStmt) {
error_node(ms->tag, "Expected an `in` assignment for this type match statement");
@@ -1005,7 +1049,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_expr(c, &x, rhs);
check_assignment(c, &x, NULL, str_lit("type match expression"));
- if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
+ match_type_kind = check_valid_type_match_type(x.type);
+ if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
gbString str = type_to_string(x.type);
error_node(x.expr,
"Invalid type for this type match expression, got `%s`", str);
@@ -1062,14 +1107,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
// TODO(bill): Make robust
Type *bt = base_type(type_deref(x.type));
-
AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
Type *case_type = NULL;
if (type_expr != NULL) { // Otherwise it's a default expression
Operand y = {0};
check_expr_or_type(c, &y, type_expr);
- if (is_union_ptr) {
+ if (match_type_kind == MatchType_Union) {
GB_ASSERT(is_type_union(bt));
bool tag_type_found = false;
for (isize i = 0; i < bt->Record.variant_count; i++) {
@@ -1086,7 +1130,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
continue;
}
case_type = y.type;
- } else if (is_any) {
+ } else if (match_type_kind == MatchType_Any) {
case_type = y.type;
} else {
GB_PANIC("Unknown type to type match statement");
@@ -1110,11 +1154,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_open_scope(c, stmt);
if (case_type == NULL) {
- if (is_union_ptr) {
- case_type = type_deref(x.type);
- } else {
- case_type = x.type;
- }
+ case_type = type_deref(x.type);
}
add_type_info_type(c, case_type);
@@ -1122,7 +1162,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
{
// NOTE(bill): Dummy type
Type *tt = case_type;
- if (is_union_ptr) {
+ if (is_type_pointer(x.type)) {
tt = make_type_pointer(c->allocator, case_type);
add_type_info_type(c, tt);
}
@@ -1173,20 +1213,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
break;
}
+
+ if (bs->label != NULL) {
+ if (bs->label->kind != AstNode_Ident) {
+ error_node(bs->label, "A branch statement's label name must be an identifier");
+ return;
+ }
+ AstNode *ident = bs->label;
+ String name = ident->Ident.string;
+ Entity *e = scope_lookup_entity(c->context.scope, name);
+ if (e == NULL) {
+ error_node(ident, "Undeclared label name: %.*s", LIT(name));
+ return;
+ }
+ add_entity_use(c, ident, e);
+ if (e->kind != Entity_Label) {
+ error_node(ident, "`%.*s` is not a label", LIT(name));
+ return;
+ }
+ }
+
case_end;
case_ast_node(us, UsingStmt, node);
- switch (us->node->kind) {
- default:
- // TODO(bill): Better error message for invalid using statement
- error(us->token, "Invalid `using` statement");
- break;
- case_ast_node(es, ExprStmt, us->node);
- // TODO(bill): Allow for just a LHS expression list rather than this silly code
+ if (us->list.count == 0) {
+ error(us->token, "Empty `using` list");
+ return;
+ }
+ for_array(i, us->list) {
+ AstNode *expr = unparen_expr(us->list.e[0]);
Entity *e = NULL;
bool is_selector = false;
- AstNode *expr = unparen_expr(es->expr);
if (expr->kind == AstNode_Ident) {
String name = expr->Ident.string;
e = scope_lookup_entity(c->context.scope, name);
@@ -1194,11 +1252,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Operand o = {0};
e = check_selector(c, &o, expr, NULL);
is_selector = true;
+ } else if (expr->kind == AstNode_Implicit) {
+ error(us->token, "`using` applied to an implicit value");
+ continue;
}
if (e == NULL) {
error(us->token, "`using` applied to an unknown entity");
- return;
+ continue;
}
switch (e->kind) {
@@ -1296,6 +1357,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(us->token, "`using` cannot be applied to `nil`");
break;
+ case Entity_Label:
+ error(us->token, "`using` cannot be applied to a label");
+ break;
+
case Entity_Invalid:
error(us->token, "`using` cannot be applied to an invalid entity");
break;
@@ -1303,13 +1368,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
default:
GB_PANIC("TODO(bill): `using` other expressions?");
}
- case_end;
-
}
case_end;
-
case_ast_node(pa, PushAllocator, node);
Operand op = {0};
check_expr(c, &op, pa->expr);
diff --git a/src/checker.c b/src/checker.c
index 881e86c8b..4cd8e7ae9 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -98,7 +98,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
// {STR_LIT("ptr_offset"), 2, false, Expr_Expr},
// {STR_LIT("ptr_sub"), 2, false, Expr_Expr},
- {STR_LIT("slice_ptr"), 2, false, Expr_Expr},
+ {STR_LIT("slice_ptr"), 2, true, Expr_Expr},
{STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt},
{STR_LIT("min"), 2, false, Expr_Expr},
@@ -163,6 +163,11 @@ bool is_operand_nil(Operand o) {
}
+typedef struct BlockLabel {
+ String name;
+ AstNode *label; // AstNode_Label
+} BlockLabel;
+
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
typedef struct DeclInfo {
Scope *scope;
@@ -175,16 +180,19 @@ typedef struct DeclInfo {
AstNode *proc_lit; // AstNode_ProcLit
MapBool deps; // Key: Entity *
+ Array(BlockLabel) labels;
} DeclInfo;
// ProcedureInfo stores the information needed for checking a procedure
+
+
typedef struct ProcedureInfo {
- AstFile * file;
- Token token;
- DeclInfo *decl;
- Type * type; // Type_Procedure
- AstNode * body; // AstNode_BlockStmt
- u32 tags;
+ AstFile * file;
+ Token token;
+ DeclInfo * decl;
+ Type * type; // Type_Procedure
+ AstNode * body; // AstNode_BlockStmt
+ u32 tags;
} ProcedureInfo;
// ExprInfo stores information used for "untyped" expressions
@@ -320,6 +328,7 @@ typedef Array(DelayedEntity) DelayedEntities;
void init_declaration_info(DeclInfo *d, Scope *scope) {
d->scope = scope;
map_bool_init(&d->deps, heap_allocator());
+ array_init(&d->labels, heap_allocator());
}
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
@@ -455,6 +464,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
if (found) {
Entity *e = *found;
if (gone_thru_proc) {
+ if (e->kind == Entity_Label) {
+ continue;
+ }
if (e->kind == Entity_Variable &&
!e->scope->is_file &&
!e->scope->is_global) {
diff --git a/src/entity.c b/src/entity.c
index 446edfa54..aa1b8f52a 100644
--- a/src/entity.c
+++ b/src/entity.c
@@ -15,12 +15,13 @@ typedef struct Type Type;
ENTITY_KIND(LibraryName) \
ENTITY_KIND(Alias) \
ENTITY_KIND(Nil) \
- ENTITY_KIND(Count)
+ ENTITY_KIND(Label)
typedef enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
ENTITY_KINDS
#undef ENTITY_KIND
+ Entity_Count,
} EntityKind;
String const entity_strings[] = {
@@ -100,6 +101,10 @@ struct Entity {
Entity *original;
} Alias;
i32 Nil;
+ struct {
+ String name;
+ AstNode *node;
+ } Label;
};
};
@@ -235,6 +240,15 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
return entity;
}
+Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
+ AstNode *node) {
+ Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
+ entity->Label.node = node;
+ return entity;
+}
+
+
+
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL, false);
diff --git a/src/exact_value.c b/src/exact_value.c
index 88e3352f3..7e8e69f50 100644
--- a/src/exact_value.c
+++ b/src/exact_value.c
@@ -82,6 +82,7 @@ ExactValue exact_value_integer_from_string(String string) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
+ case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
@@ -100,14 +101,10 @@ ExactValue exact_value_integer_from_string(String string) {
continue;
}
i64 v = 0;
- if (gb_char_is_digit(r)) {
- v = r - '0';
- } else if (gb_char_is_hex_digit(r)) {
- v = gb_hex_digit_to_int(r);
- } else {
+ v = digit_value(r);
+ if (v >= base) {
break;
}
-
result *= base;
result += v;
}
@@ -137,10 +134,10 @@ ExactValue exact_value_float_from_string(String string) {
if (r == '_') {
continue;
}
- if (!gb_char_is_digit(r)) {
+ i64 v = digit_value(r);
+ if (v >= 10) {
break;
}
- i64 v = r - '0';
value *= 10.0;
value += v;
}
@@ -153,29 +150,38 @@ ExactValue exact_value_float_from_string(String string) {
if (r == '_') {
continue;
}
- if (!gb_char_is_digit(r)) {
+ i64 v = digit_value(r);
+ if (v >= 10) {
break;
}
- value += (r-'0')/pow10;
+ value += v/pow10;
pow10 *= 10.0;
}
}
- f64 frac = 0;
+ bool frac = false;
f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) {
i++;
if (str[i] == '-') {
- frac = 1;
+ frac = true;
i++;
} else if (str[i] == '+') {
i++;
}
- u32 exp;
- for (exp = 0; gb_char_is_digit(str[i]); i++) {
- exp = exp * 10 + (str[i]-'0');
+ u32 exp = 0;
+ for (; i < len; i++) {
+ Rune r = cast(Rune)str[i];
+ if (r == '_') {
+ continue;
+ }
+ u32 d = cast(u32)digit_value(r);
+ if (d >= 10) {
+ break;
+ }
+ exp = exp * 10 + d;
}
if (exp > 308) exp = 308;
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 4b048e60d..aa85e5c1d 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -811,6 +811,10 @@ GB_DEF void const *gb_memrchr (void const *data, u8 byte_value, isize size);
#define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count))
#endif
+#ifndef gb_memmove_array
+#define gb_memmove_array(dst, src, count) gb_memmove((dst), (src), gb_size_of(*(dst))*(count))
+#endif
+
// NOTE(bill): Very similar to doing `*cast(T *)(&u)`
#ifndef GB_BIT_CAST
#define GB_BIT_CAST(dest, source) do { \
diff --git a/src/ir.c b/src/ir.c
index 9d44d0188..d0e650ad4 100644
--- a/src/ir.c
+++ b/src/ir.c
@@ -91,7 +91,7 @@ typedef enum irDeferKind {
typedef struct irDefer {
irDeferKind kind;
- isize scope_index;
+ isize scope_index;
irBlock * block;
union {
AstNode *stmt;
@@ -100,32 +100,41 @@ typedef struct irDefer {
};
} irDefer;
+
+typedef struct irBranchBlocks {
+ AstNode *label;
+ irBlock *break_;
+ irBlock *continue_;
+} irBranchBlocks;
+
+
struct irProcedure {
- irProcedure * parent;
- Array(irProcedure *) children;
-
- 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;
-
-
- i32 local_count;
- i32 instr_count;
- i32 block_count;
+ irProcedure * parent;
+ Array(irProcedure *) children;
+
+ 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;
+
+ Array(irBranchBlocks) branch_blocks;
+
+ 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_;
- }
- break;
- case Token_continue:
- for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
- block = t->continue_;
+
+ 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_fallthrough:
- for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
- block = t->fallthrough_;
+ } 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;
}
if (block != NULL) {
ir_emit_defer_stmts(proc, irDeferExit_Branch, block);
@@ -5692,9 +5789,20 @@ void ir_number_proc_registers(irProcedure *proc) {
void ir_begin_procedure_body(irProcedure *proc) {
array_add(&proc->module->procs, proc);
- array_init(&proc->blocks, heap_allocator());
- array_init(&proc->defer_stmts, heap_allocator());
- array_init(&proc->children, heap_allocator());
+ array_init(&proc->blocks, heap_allocator());
+ array_init(&proc->defer_stmts, heap_allocator());
+ array_init(&proc->children, heap_allocator());
+ array_init(&proc->branch_blocks, heap_allocator());
+
+ DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity));
+ if (found != NULL) {
+ DeclInfo *decl = *found;
+ for_array(i, decl->labels) {
+ BlockLabel bl = decl->labels.e[i];
+ irBranchBlocks bb = {bl.label, NULL, NULL};
+ array_add(&proc->branch_blocks, bb);
+ }
+ }
proc->decl_block = ir_new_block(proc, proc->type_expr, "decls");
ir_start_block(proc, proc->decl_block);
diff --git a/src/parser.c b/src/parser.c
index d80e2380d..83948e504 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -219,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \
AST_NODE_KIND(ForStmt, "for statement", struct { \
Token token; \
+ AstNode *label; \
AstNode *init; \
AstNode *cond; \
AstNode *post; \
@@ -226,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \
AST_NODE_KIND(RangeStmt, "range statement", struct { \
Token token; \
+ AstNode *label; \
AstNode *value; \
AstNode *index; \
Token in_token; \
@@ -249,10 +251,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
AstNode *body; \
}) \
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
- AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
+ AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
AST_NODE_KIND(UsingStmt, "using statement", struct { \
Token token; \
- AstNode *node; \
+ AstNodeArray list; \
}) \
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
Token string; \
@@ -305,6 +307,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
AstNode *cond; \
bool is_system; \
}) \
+ AST_NODE_KIND(Label, "label", struct { \
+ Token token; \
+ AstNode *name; \
+ }) \
AST_NODE_KIND(_DeclEnd, "", i32) \
AST_NODE_KIND(Field, "field", struct { \
AstNodeArray names; \
@@ -499,6 +505,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]);
case AstNode_ImportDecl: return node->ImportDecl.token;
case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
+ case AstNode_Label: return node->Label.token;
case AstNode_Field:
@@ -869,9 +876,10 @@ AstNode *ast_return_stmt(AstFile *f, Token token, AstNodeArray results) {
}
-AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
+AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *label, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
AstNode *result = make_ast_node(f, AstNode_ForStmt);
result->ForStmt.token = token;
+ result->ForStmt.label = label;
result->ForStmt.init = init;
result->ForStmt.cond = cond;
result->ForStmt.post = post;
@@ -879,8 +887,9 @@ AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
return result;
}
-AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
+AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *label, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
AstNode *result = make_ast_node(f, AstNode_RangeStmt);
+ result->RangeStmt.label = label;
result->RangeStmt.token = token;
result->RangeStmt.value = value;
result->RangeStmt.index = index;
@@ -924,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
return result;
}
-AstNode *ast_branch_stmt(AstFile *f, Token token) {
+AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
AstNode *result = make_ast_node(f, AstNode_BranchStmt);
result->BranchStmt.token = token;
+ result->BranchStmt.label = label;
return result;
}
-AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) {
+AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
AstNode *result = make_ast_node(f, AstNode_UsingStmt);
result->UsingStmt.token = token;
- result->UsingStmt.node = node;
+ result->UsingStmt.list = list;
return result;
}
+
AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
AstNode *result = make_ast_node(f, AstNode_AsmOperand);
result->AsmOperand.string = string;
@@ -1138,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
return result;
}
+AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
+ AstNode *result = make_ast_node(f, AstNode_Label);
+ result->Label.token = token;
+ result->Label.name = name;
+ return result;
+}
+
bool next_token(AstFile *f) {
Token prev = f->curr_token;
@@ -2335,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
return parse_body(f);
}
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow);
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
AstNode *parse_results(AstFile *f) {
@@ -2354,7 +2372,7 @@ AstNode *parse_results(AstFile *f) {
AstNode *list = NULL;
expect_token(f, Token_OpenParen);
- list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen);
+ list = parse_field_list(f, NULL, 0, Token_CloseParen);
expect_token_after(f, Token_CloseParen, "parameter list");
return list;
}
@@ -2365,7 +2383,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
Token proc_token = expect_token(f, Token_proc);
expect_token(f, Token_OpenParen);
- params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
+ params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
expect_token_after(f, Token_CloseParen, "parameter list");
results = parse_results(f);
@@ -2384,17 +2402,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
return ast_proc_type(f, proc_token, params, results, tags, cc);
}
-bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
- if (separator == Token_Semicolon) {
- expect_semicolon(f, param);
- } else {
- if (!allow_token(f, separator)) {
- return true;
- }
- }
- return false;
-}
-
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
Token tok = f->curr_token;
@@ -2505,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
return idents;
}
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
+
+bool parse_expect_field_separator(AstFile *f, AstNode *param) {
+ Token token = f->curr_token;
+ if (allow_token(f, Token_Comma)) {
+ return true;
+ }
+ if (token.kind == Token_Semicolon) {
+ next_token(f);
+ error(f->curr_token, "Expected a comma, got a semicolon");
+ return true;
+ }
+ return false;
+}
+
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
+ TokenKind separator = Token_Comma;
Token start_token = f->curr_token;
AstNodeArray params = make_ast_node_array(f);
@@ -2543,7 +2565,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
AstNode *param = ast_field(f, names, type, set_flags);
array_add(&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");
- } else {
- if (f->curr_proc == NULL) {
- syntax_error(token, "`using` is not allowed at the file scope");
- } else {
- node->ValueDecl.flags |= VarDeclFlag_using;
- }
+ return decl;
}
- 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);
+ if (f->curr_proc == NULL) {
+ syntax_error(token, "`using` is not allowed at the file scope");
+ } else {
+ decl->ValueDecl.flags |= VarDeclFlag_using;
}
- } break;
-
-
+ return decl;
}
syntax_error(token, "Illegal use of `using` statement");
@@ -3518,6 +3544,20 @@ AstNode *parse_stmt(AstFile *f) {
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
}
return s;
+ } else if (str_eq(tag, str_lit("label"))) {
+ AstNode *name = parse_ident(f);
+ AstNode *label = ast_label_decl(f, token, name);
+
+ Token tok = f->curr_token;
+ switch (tok.kind) {
+ case Token_for:
+ return parse_for_stmt(f, label);
+ default:
+ syntax_error(token, "#label may only be applied to a loop");
+ fix_advance_to_next_stmt(f);
+ s = ast_bad_stmt(f, token, f->curr_token);
+ return s;
+ }
}
diff --git a/src/ssa.c b/src/ssa.c
index 67c16a1e4..2ed9d59f9 100644
--- a/src/ssa.c
+++ b/src/ssa.c
@@ -1,4 +1,3 @@
-typedef enum ssaOp ssaOp;
typedef struct ssaModule ssaModule;
typedef struct ssaValue ssaValue;
typedef struct ssaValueArgs ssaValueArgs;
@@ -19,282 +18,7 @@ String ssa_mangle_name(ssaModule *m, String path, Entity *e);
typedef Array(ssaValue *) ssaValueArray;
-
-#define SSA_OPS \
- SSA_OP(Invalid)\
-\
- SSA_OP(Unknown)\
-\
- SSA_OP(Comment) /* Does nothing */\
-\
- SSA_OP(SP) /* Stack Pointer */\
- SSA_OP(SB) /* Stack Base */\
- SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\
-\
- SSA_OP(Local)\
- SSA_OP(Global)\
- SSA_OP(Proc)\
-\
- SSA_OP(Load)\
- SSA_OP(Store)\
- SSA_OP(Move)\
- SSA_OP(Zero) /* Zero initialize */\
-\
- SSA_OP(ArrayIndex) /* Index for a fixed array */\
- SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
- SSA_OP(OffsetPtr)\
- SSA_OP(ValueIndex) /* Extract for a value from a register */\
-\
- SSA_OP(Phi)\
- SSA_OP(Copy)\
-\
- /* TODO(bill): calling conventions */\
- SSA_OP(CallOdin)\
- SSA_OP(CallC)\
- SSA_OP(CallStd)\
- SSA_OP(CallFast)\
-\
- SSA_OP(BoundsCheck)\
- SSA_OP(SliceBoundsCheck)\
-\
- /* Built in operations/procedures */\
- SSA_OP(Bswap16)\
- SSA_OP(Bswap32)\
- SSA_OP(Bswap64)\
-\
- SSA_OP(Assume)\
- SSA_OP(DebugTrap)\
- SSA_OP(Trap)\
- SSA_OP(ReadCycleCounter)\
-\
-\
- SSA_OP(ConstBool)\
- SSA_OP(ConstString)\
- SSA_OP(ConstSlice)\
- SSA_OP(ConstNil)\
- SSA_OP(Const8)\
- SSA_OP(Const16)\
- SSA_OP(Const32)\
- SSA_OP(Const64)\
- SSA_OP(Const32F)\
- SSA_OP(Const64F)\
-\
- /* These should be all the operations I could possibly need for the mean time */\
- SSA_OP(Add8)\
- SSA_OP(Add16)\
- SSA_OP(Add32)\
- SSA_OP(Add64)\
- SSA_OP(AddPtr)\
- SSA_OP(Add32F)\
- SSA_OP(Add64F)\
- SSA_OP(Sub8)\
- SSA_OP(Sub16)\
- SSA_OP(Sub32)\
- SSA_OP(Sub64)\
- SSA_OP(SubPtr)\
- SSA_OP(Sub32F)\
- SSA_OP(Sub64F)\
- SSA_OP(Mul8)\
- SSA_OP(Mul16)\
- SSA_OP(Mul32)\
- SSA_OP(Mul64)\
- SSA_OP(Mul32F)\
- SSA_OP(Mul64F)\
- SSA_OP(Div8)\
- SSA_OP(Div8U)\
- SSA_OP(Div16)\
- SSA_OP(Div16U)\
- SSA_OP(Div32)\
- SSA_OP(Div32U)\
- SSA_OP(Div64)\
- SSA_OP(Div64U)\
- SSA_OP(Div32F)\
- SSA_OP(Div64F)\
- SSA_OP(Mod8)\
- SSA_OP(Mod8U)\
- SSA_OP(Mod16)\
- SSA_OP(Mod16U)\
- SSA_OP(Mod32)\
- SSA_OP(Mod32U)\
- SSA_OP(Mod64)\
- SSA_OP(Mod64U)\
-\
- SSA_OP(And8)\
- SSA_OP(And16)\
- SSA_OP(And32)\
- SSA_OP(And64)\
- SSA_OP(Or8)\
- SSA_OP(Or16)\
- SSA_OP(Or32)\
- SSA_OP(Or64)\
- SSA_OP(Xor8)\
- SSA_OP(Xor16)\
- SSA_OP(Xor32)\
- SSA_OP(Xor64)\
- SSA_OP(AndNot8)\
- SSA_OP(AndNot16)\
- SSA_OP(AndNot32)\
- SSA_OP(AndNot64)\
-\
- SSA_OP(Lsh8x8)\
- SSA_OP(Lsh8x16)\
- SSA_OP(Lsh8x32)\
- SSA_OP(Lsh8x64)\
- SSA_OP(Lsh16x8)\
- SSA_OP(Lsh16x16)\
- SSA_OP(Lsh16x32)\
- SSA_OP(Lsh16x64)\
- SSA_OP(Lsh32x8)\
- SSA_OP(Lsh32x16)\
- SSA_OP(Lsh32x32)\
- SSA_OP(Lsh32x64)\
- SSA_OP(Lsh64x8)\
- SSA_OP(Lsh64x16)\
- SSA_OP(Lsh64x32)\
- SSA_OP(Lsh64x64)\
- SSA_OP(Rsh8x8)\
- SSA_OP(Rsh8x16)\
- SSA_OP(Rsh8x32)\
- SSA_OP(Rsh8x64)\
- SSA_OP(Rsh16x8)\
- SSA_OP(Rsh16x16)\
- SSA_OP(Rsh16x32)\
- SSA_OP(Rsh16x64)\
- SSA_OP(Rsh32x8)\
- SSA_OP(Rsh32x16)\
- SSA_OP(Rsh32x32)\
- SSA_OP(Rsh32x64)\
- SSA_OP(Rsh64x8)\
- SSA_OP(Rsh64x16)\
- SSA_OP(Rsh64x32)\
- SSA_OP(Rsh64x64)\
- SSA_OP(Rsh8Ux8)\
- SSA_OP(Rsh8Ux16)\
- SSA_OP(Rsh8Ux32)\
- SSA_OP(Rsh8Ux64)\
- SSA_OP(Rsh16Ux8)\
- SSA_OP(Rsh16Ux16)\
- SSA_OP(Rsh16Ux32)\
- SSA_OP(Rsh16Ux64)\
- SSA_OP(Rsh32Ux8)\
- SSA_OP(Rsh32Ux16)\
- SSA_OP(Rsh32Ux32)\
- SSA_OP(Rsh32Ux64)\
- SSA_OP(Rsh64Ux8)\
- SSA_OP(Rsh64Ux16)\
- SSA_OP(Rsh64Ux32)\
- SSA_OP(Rsh64Ux64)\
-\
- SSA_OP(Eq8)\
- SSA_OP(Eq16)\
- SSA_OP(Eq32)\
- SSA_OP(Eq64)\
- SSA_OP(EqPtr)\
- SSA_OP(Eq32F)\
- SSA_OP(Eq64F)\
- SSA_OP(Ne8)\
- SSA_OP(Ne16)\
- SSA_OP(Ne32)\
- SSA_OP(Ne64)\
- SSA_OP(NePtr)\
- SSA_OP(Ne32F)\
- SSA_OP(Ne64F)\
- SSA_OP(Lt8)\
- SSA_OP(Lt16)\
- SSA_OP(Lt32)\
- SSA_OP(Lt64)\
- SSA_OP(LtPtr)\
- SSA_OP(Lt32F)\
- SSA_OP(Lt64F)\
- SSA_OP(Gt8)\
- SSA_OP(Gt16)\
- SSA_OP(Gt32)\
- SSA_OP(Gt64)\
- SSA_OP(GtPtr)\
- SSA_OP(Gt32F)\
- SSA_OP(Gt64F)\
- SSA_OP(Le8)\
- SSA_OP(Le16)\
- SSA_OP(Le32)\
- SSA_OP(Le64)\
- SSA_OP(LePtr)\
- SSA_OP(Le32F)\
- SSA_OP(Le64F)\
- SSA_OP(Ge8)\
- SSA_OP(Ge16)\
- SSA_OP(Ge32)\
- SSA_OP(Ge64)\
- SSA_OP(GePtr)\
- SSA_OP(Ge32F)\
- SSA_OP(Ge64F)\
-\
- SSA_OP(NotB)\
- SSA_OP(EqB)\
- SSA_OP(NeB)\
-\
- SSA_OP(Neg8)\
- SSA_OP(Neg16)\
- SSA_OP(Neg32)\
- SSA_OP(Neg64)\
- SSA_OP(Neg32F)\
- SSA_OP(Neg64F)\
-\
- SSA_OP(Not8)\
- SSA_OP(Not16)\
- SSA_OP(Not32)\
- SSA_OP(Not64)\
-\
- SSA_OP(SignExt8to16)\
- SSA_OP(SignExt8to32)\
- SSA_OP(SignExt8to64)\
- SSA_OP(SignExt16to32)\
- SSA_OP(SignExt16to64)\
- SSA_OP(SignExt32to64)\
- SSA_OP(ZeroExt8to16)\
- SSA_OP(ZeroExt8to32)\
- SSA_OP(ZeroExt8to64)\
- SSA_OP(ZeroExt16to32)\
- SSA_OP(ZeroExt16to64)\
- SSA_OP(ZeroExt32to64)\
- SSA_OP(Trunc16to8)\
- SSA_OP(Trunc32to8)\
- SSA_OP(Trunc32to16)\
- SSA_OP(Trunc64to8)\
- SSA_OP(Trunc64to16)\
- SSA_OP(Trunc64to32)\
-\
- SSA_OP(Cvt32to32F)\
- SSA_OP(Cvt32to64F)\
- SSA_OP(Cvt64to32F)\
- SSA_OP(Cvt64to64F)\
- SSA_OP(Cvt32Fto32)\
- SSA_OP(Cvt32Fto64)\
- SSA_OP(Cvt64Fto32)\
- SSA_OP(Cvt64Fto64)\
- SSA_OP(Cvt32Fto64F)\
- SSA_OP(Cvt64Fto32F)\
- SSA_OP(Cvt32Uto32F)\
- SSA_OP(Cvt32Uto64F)\
- SSA_OP(Cvt32Fto32U)\
- SSA_OP(Cvt64Fto32U)\
- SSA_OP(Cvt64Uto32F)\
- SSA_OP(Cvt64Uto64F)\
- SSA_OP(Cvt32Fto64U)\
- SSA_OP(Cvt64Fto64U)\
-
-
-enum ssaOp {
-#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
- SSA_OPS
-#undef SSA_OP
-};
-
-String const ssa_op_strings[] = {
-#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
- SSA_OPS
-#undef SSA_OP
-};
-
+#include "ssa_op.c"
#define SSA_DEFAULT_VALUE_ARG_CAPACITY 8
struct ssaValueArgs {
@@ -379,6 +103,7 @@ struct ssaTargetList {
struct ssaProc {
ssaModule * module; // Parent module
+ gbAllocator allocator; // Same allocator as the parent module
String name; // Mangled name
Entity * entity;
DeclInfo * decl_info;
@@ -424,7 +149,7 @@ struct ssaModule {
void ssa_push_target_list(ssaProc *p, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
- ssaTargetList *tl = gb_alloc_item(p->module->allocator, ssaTargetList);
+ ssaTargetList *tl = gb_alloc_item(p->allocator, ssaTargetList);
tl->prev = p->target_list;
tl->break_ = break_;
tl->continue_ = continue_;
@@ -438,7 +163,7 @@ void ssa_pop_target_list(ssaProc *p) {
ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind, char *name) {
- ssaBlock *b = gb_alloc_item(p->module->allocator, ssaBlock);
+ ssaBlock *b = gb_alloc_item(p->allocator, ssaBlock);
b->id = p->block_id++;
b->kind = kind;
b->proc = p;
@@ -531,64 +256,68 @@ void ssa_add_arg(ssaValueArgs *va, ssaValue *arg) {
ssaValue *ssa_new_value(ssaProc *p, ssaOp op, Type *t, ssaBlock *b) {
- ssaValue *v = gb_alloc_item(p->module->allocator, ssaValue);
+ GB_ASSERT(b != NULL);
+ ssaValue *v = gb_alloc_item(p->allocator, ssaValue);
v->id = p->value_id++;
v->op = op;
v->type = t;
v->block = b;
- ssa_init_value_args(&v->args, p->module->allocator);
+ ssa_init_value_args(&v->args, p->allocator);
array_add(&b->values, v);
return v;
}
-ssaValue *ssa_new_value0(ssaBlock *b, ssaOp op, Type *t) {
- ssaValue *v = ssa_new_value(b->proc, op, t, b);
+ssaValue *ssa_new_value0(ssaProc *p, ssaOp op, Type *t) {
+ ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
return v;
}
-ssaValue *ssa_new_value0v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value) {
- ssaValue *v = ssa_new_value0(b, op, t);
+ssaValue *ssa_new_value0v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) {
+ ssaValue *v = ssa_new_value0(p, op, t);
v->exact_value = exact_value;
return v;
}
-ssaValue *ssa_new_value1(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg) {
- ssaValue *v = ssa_new_value(b->proc, op, t, b);
+ssaValue *ssa_new_value1(ssaProc *p, ssaOp op, Type *t, ssaValue *arg) {
+ ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
ssa_add_arg(&v->args, arg);
return v;
}
-ssaValue *ssa_new_value1v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) {
- ssaValue *v = ssa_new_value1(b, op, t, arg);
+ssaValue *ssa_new_value1v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) {
+ ssaValue *v = ssa_new_value1(p, op, t, arg);
v->exact_value = exact_value;
return v;
}
+ssaValue *ssa_new_value1i(ssaProc *p, ssaOp op, Type *t, i64 i, ssaValue *arg) {
+ return ssa_new_value1v(p, op, t, exact_value_integer(i), arg);
+}
-ssaValue *ssa_new_value2(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) {
- ssaValue *v = ssa_new_value(b->proc, op, t, b);
+ssaValue *ssa_new_value2(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) {
+ ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
ssa_add_arg(&v->args, arg0);
ssa_add_arg(&v->args, arg1);
return v;
}
-ssaValue *ssa_new_value2v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) {
- ssaValue *v = ssa_new_value2(b, op, t, arg0, arg1);
+ssaValue *ssa_new_value2v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) {
+ ssaValue *v = ssa_new_value2(p, op, t, arg0, arg1);
v->exact_value = exact_value;
return v;
}
-ssaValue *ssa_new_value3(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
- ssaValue *v = ssa_new_value(b->proc, op, t, b);
+ssaValue *ssa_new_value3(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
+ ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
ssa_add_arg(&v->args, arg0);
ssa_add_arg(&v->args, arg1);
ssa_add_arg(&v->args, arg2);
return v;
}
-ssaValue *ssa_new_value3v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
- ssaValue *v = ssa_new_value3(b, op, t, arg0, arg1, arg2);
+ssaValue *ssa_new_value3v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
+ ssaValue *v = ssa_new_value3(p, op, t, arg0, arg1, arg2);
v->exact_value = exact_value;
return v;
}
-ssaValue *ssa_new_value4(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) {
- ssaValue *v = ssa_new_value(b->proc, op, t, b);
+ssaValue *ssa_new_value4(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) {
+ ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
ssa_add_arg(&v->args, arg0);
ssa_add_arg(&v->args, arg1);
ssa_add_arg(&v->args, arg2);
@@ -597,7 +326,7 @@ ssaValue *ssa_new_value4(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValu
}
ssaValue *ssa_const_val(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) {
- return ssa_new_value0v(p->curr_block, op, t, exact_value);
+ return ssa_new_value0v(p, op, t, exact_value);
}
ssaValue *ssa_const_bool (ssaProc *p, Type *t, bool c) { return ssa_const_val(p, ssaOp_ConstBool, t, exact_value_bool(c)); }
@@ -613,7 +342,7 @@ ssaValue *ssa_const_slice (ssaProc *p, Type *t, ExactValue v) { return ssa
ssaValue *ssa_const_nil (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstNil, t, (ExactValue){0}); }
ssaValue *ssa_const_int(ssaProc *p, Type *t, i64 c) {
- switch (8*type_size_of(p->module->allocator, t)) {
+ switch (8*type_size_of(p->allocator, t)) {
case 8: return ssa_const_i8 (p, t, cast(i8)c);
case 16: return ssa_const_i16(p, t, cast(i16)c);
case 32: return ssa_const_i32(p, t, cast(i32)c);
@@ -630,6 +359,47 @@ void ssa_reset_value_args(ssaValue *v) {
v->args.count = 0;
}
+void ssa_reset(ssaValue *v, ssaOp op) {
+ v->op = op;
+ v->exact_value = (ExactValue){0};
+ ssa_reset_value_args(v);
+}
+
+ssaValue *ssa_emit_load(ssaProc *p, ssaValue *v) {
+ GB_ASSERT(is_type_pointer(v->type));
+ return ssa_new_value1(p, ssaOp_Load, type_deref(v->type), v);
+}
+
+ssaValue *ssa_emit_store(ssaProc *p, ssaValue *dst, ssaValue *v) {
+ GB_ASSERT(is_type_pointer(dst->type));
+#if 1
+ // NOTE(bill): Sanity check
+ Type *a = core_type(type_deref(dst->type));
+ Type *b = core_type(v->type);
+ if (!is_type_untyped(b)) {
+ GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b));
+ }
+#endif
+ return ssa_new_value2(p, ssaOp_Store, dst->type, dst, v);
+}
+
+bool ssa_is_op_const(ssaOp op) {
+ switch (op) {
+ case ssaOp_ConstBool:
+ case ssaOp_ConstString:
+ case ssaOp_ConstSlice:
+ case ssaOp_ConstNil:
+ case ssaOp_Const8:
+ case ssaOp_Const16:
+ case ssaOp_Const32:
+ case ssaOp_Const64:
+ case ssaOp_Const32F:
+ case ssaOp_Const64F:
+ return true;
+ }
+ return false;
+}
+
bool ssa_is_blank_ident(AstNode *node) {
@@ -680,6 +450,7 @@ Type *ssa_addr_type(ssaAddr addr) {
ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_info) {
ssaProc *p = gb_alloc_item(m->allocator, ssaProc);
p->module = m;
+ p->allocator = m->allocator;
p->name = name;
p->entity = entity;
p->decl_info = decl_info;
@@ -691,14 +462,19 @@ ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_
}
ssaAddr ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) {
- Type *t = make_type_pointer(p->module->allocator, e->type);
- ssaValue *local = ssa_new_value0(p->entry, ssaOp_Local, t);
+ Type *t = make_type_pointer(p->allocator, e->type);
+
+ ssaBlock *cb = p->curr_block;
+ p->curr_block = p->entry;
+ ssaValue *local = ssa_new_value0(p, ssaOp_Local, t);
+ p->curr_block = cb;
+
map_ssa_value_set(&p->values, hash_pointer(e), local);
map_ssa_value_set(&p->module->values, hash_pointer(e), local);
local->comment_string = e->token.string;
- ssaValue *addr = ssa_new_value1(p->curr_block, ssaOp_Addr, local->type, local);
- ssa_new_value1(p->curr_block, ssaOp_Zero, t, addr);
+ ssaValue *addr = ssa_new_value1(p, ssaOp_Addr, local->type, local);
+ ssa_new_value1(p, ssaOp_Zero, t, addr);
return ssa_addr(addr);
}
ssaAddr ssa_add_local_for_ident(ssaProc *p, AstNode *name) {
@@ -718,12 +494,12 @@ ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) {
if (p->curr_block) {
// scope = p->curr_block->scope;
}
- Entity *e = make_entity_variable(p->module->allocator, scope, empty_token, t, false);
+ Entity *e = make_entity_variable(p->allocator, scope, empty_token, t, false);
return ssa_add_local(p, e, NULL);
}
void ssa_emit_comment(ssaProc *p, String s) {
- // ssa_new_value0v(p->curr_block, ssaOp_Comment, NULL, exact_value_string(s));
+ // ssa_new_value0v(p, ssaOp_Comment, NULL, exact_value_string(s));
}
#define SSA_MAX_STRUCT_FIELD_COUNT 4
@@ -773,10 +549,11 @@ bool can_ssa_type(Type *t) {
return true;
}
-
-
-void ssa_build_stmt(ssaProc *p, AstNode *node);
-void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes);
+ssaAddr ssa_build_addr (ssaProc *p, AstNode *expr);
+ssaValue *ssa_build_expr (ssaProc *p, AstNode *expr);
+void ssa_build_stmt (ssaProc *p, AstNode *node);
+void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes);
+ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel);
void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) {
if (addr.addr == NULL) {
@@ -787,7 +564,7 @@ void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) {
return;
}
- ssa_new_value2(p->curr_block, ssaOp_Store, addr.addr->type, addr.addr, value);
+ ssa_emit_store(p, addr.addr, value);
}
ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) {
@@ -806,26 +583,25 @@ ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) {
return addr.addr;
}
- return ssa_new_value1(p->curr_block, ssaOp_Load, type_deref(t), addr.addr);
+ return ssa_emit_load(p, addr.addr);
}
ssaValue *ssa_get_using_variable(ssaProc *p, Entity *e) {
- GB_PANIC("TODO(bill): ssa_get_using_variable");
- return NULL;
- // GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
- // String name = e->token.string;
- // Entity *parent = e->using_parent;
- // Selection sel = lookup_field(proc->module->allocator, parent->type, name, false);
- // GB_ASSERT(sel.entity != NULL);
- // irValue **pv = map_ir_value_get(&proc->module->values, hash_pointer(parent));
- // irValue *v = NULL;
- // if (pv != NULL) {
- // v = *pv;
- // } else {
- // v = ir_build_addr(proc, e->using_expr).addr;
- // }
- // GB_ASSERT(v != NULL);
- // return ir_emit_deep_field_gep(proc, parent->type, v, sel);
+ GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
+ String name = e->token.string;
+ Entity *parent = e->using_parent;
+ Selection sel = lookup_field(p->allocator, parent->type, name, false);
+ GB_ASSERT(sel.entity != NULL);
+ ssaValue **pv = map_ssa_value_get(&p->module->values, hash_pointer(parent));
+ ssaValue *v = NULL;
+ if (pv != NULL) {
+ v = *pv;
+ } else {
+ v = ssa_build_addr(p, e->using_expr).addr;
+ }
+ GB_ASSERT(v != NULL);
+ GB_ASSERT(type_deref(v->type) == parent->type);
+ return ssa_emit_deep_field_ptr_index(p, v, sel);
}
ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) {
@@ -847,6 +623,311 @@ ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) {
return ssa_addr(v);
}
+
+ssaValue *ssa_emit_conv(ssaProc *p, ssaValue *v, Type *t) {
+ Type *src_type = v->type;
+ if (are_types_identical(t, src_type)) {
+ return v;
+ }
+
+ Type *src = core_type(src_type);
+ Type *dst = core_type(t);
+
+ if (is_type_untyped_nil(src)) {
+ return ssa_const_nil(p, t);
+ }
+
+ // Pointer <-> Pointer
+ if (is_type_pointer(src) && is_type_pointer(dst)) {
+ return ssa_new_value1(p, ssaOp_Copy, dst, v);
+ }
+ // proc <-> proc
+ if (is_type_proc(src) && is_type_proc(dst)) {
+ return ssa_new_value1(p, ssaOp_Copy, dst, v);
+ }
+ // pointer -> proc
+ if (is_type_pointer(src) && is_type_proc(dst)) {
+ return ssa_new_value1(p, ssaOp_Copy, dst, v);
+ }
+ // proc -> pointer
+ if (is_type_proc(src) && is_type_pointer(dst)) {
+ return ssa_new_value1(p, ssaOp_Copy, dst, v);
+ }
+
+
+ gb_printf_err("ssa_emit_conv: src -> dst\n");
+ gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
+ gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
+
+
+ GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
+
+ return NULL;
+}
+
+
+// NOTE(bill): Returns NULL if not possible
+ssaValue *ssa_address_from_load_or_generate_local(ssaProc *p, ssaValue *v) {
+ if (v->op == ssaOp_Load) {
+ return v->args.e[0];
+ }
+ ssaAddr addr = ssa_add_local_generated(p, v->type);
+ ssa_new_value2(p, ssaOp_Store, addr.addr->type, addr.addr, v);
+ return addr.addr;
+}
+
+
+ssaValue *ssa_emit_array_index(ssaProc *p, ssaValue *v, ssaValue *index) {
+ GB_ASSERT(v != NULL);
+ GB_ASSERT(is_type_pointer(v->type));
+ Type *t = base_type(type_deref(v->type));
+ GB_ASSERT_MSG(is_type_array(t) || is_type_vector(t), "%s", type_to_string(t));
+ Type *elem_ptr = NULL;
+ if (is_type_array(t)) {
+ elem_ptr = make_type_pointer(p->allocator, t->Array.elem);
+ } else if (is_type_vector(t)) {
+ elem_ptr = make_type_pointer(p->allocator, t->Vector.elem);
+ }
+
+ return ssa_new_value2(p, ssaOp_ArrayIndex, elem_ptr, v, index);
+}
+
+ssaValue *ssa_emit_ptr_index(ssaProc *p, ssaValue *s, i64 index) {
+ gbAllocator a = p->allocator;
+ Type *t = base_type(type_deref(s->type));
+ Type *result_type = NULL;
+
+ if (is_type_struct(t)) {
+ GB_ASSERT(t->Record.field_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+ result_type = make_type_pointer(a, t->Record.fields[index]->type);
+ } else if (is_type_union(t)) {
+ type_set_offsets(a, t);
+ GB_ASSERT(t->Record.field_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+ result_type = make_type_pointer(a, t->Record.fields[index]->type);
+ i64 offset = t->Record.offsets[index];
+ ssaValue *ptr = ssa_emit_conv(p, s, t_u8_ptr);
+ ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
+ return ssa_emit_conv(p, ptr, result_type);
+ } else if (is_type_tuple(t)) {
+ GB_ASSERT(t->Tuple.variable_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+ result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
+ } else if (is_type_slice(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
+ case 1: result_type = make_type_pointer(a, t_int); break;
+ case 2: result_type = make_type_pointer(a, t_int); break;
+ }
+ } else if (is_type_string(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
+ case 1: result_type = make_type_pointer(a, t_int); break;
+ }
+ } else if (is_type_any(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
+ case 1: result_type = make_type_pointer(a, t_rawptr); break;
+ }
+ } else if (is_type_dynamic_array(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->DynamicArray.elem)); break;
+ case 1: result_type = t_int_ptr; break;
+ case 2: result_type = t_int_ptr; break;
+ case 3: result_type = t_allocator_ptr; break;
+ }
+ } else if (is_type_dynamic_map(t)) {
+ Type *gst = t->Map.generated_struct_type;
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, gst->Record.fields[0]->type); break;
+ case 1: result_type = make_type_pointer(a, gst->Record.fields[1]->type); break;
+ }
+ }else {
+ GB_PANIC("TODO(bill): ssa_emit_ptr_index type: %s, %d", type_to_string(s->type), index);
+ }
+
+ GB_ASSERT(result_type != NULL);
+
+ return ssa_new_value1i(p, ssaOp_PtrIndex, result_type, index, s);
+}
+ssaValue *ssa_emit_value_index(ssaProc *p, ssaValue *s, i64 index) {
+ if (s->op == ssaOp_Load) {
+ if (!can_ssa_type(s->type)) {
+ ssaValue *e = ssa_emit_ptr_index(p, s->args.e[0], index);
+ return ssa_emit_load(p, e);
+ }
+ }
+ GB_ASSERT(can_ssa_type(s->type));
+
+ gbAllocator a = p->allocator;
+ Type *t = base_type(s->type);
+ Type *result_type = NULL;
+
+ if (is_type_struct(t)) {
+ GB_ASSERT(t->Record.field_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+ result_type = t->Record.fields[index]->type;
+ } else if (is_type_union(t)) {
+ type_set_offsets(a, t);
+ GB_ASSERT(t->Record.field_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+ Type *ptr_type = make_type_pointer(a, t->Record.fields[index]->type);
+ i64 offset = t->Record.offsets[index];
+ ssaValue *ptr = ssa_address_from_load_or_generate_local(p, s);
+ ptr = ssa_emit_conv(p, s, t_u8_ptr);
+ ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
+ ptr = ssa_emit_conv(p, ptr, ptr_type);
+ return ssa_emit_load(p, ptr);
+ } else if (is_type_tuple(t)) {
+ GB_ASSERT(t->Tuple.variable_count > 0);
+ GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+ result_type = t->Tuple.variables[index]->type;
+ } else if (is_type_slice(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
+ case 1: result_type = t_int; break;
+ case 2: result_type = t_int; break;
+ }
+ } else if (is_type_string(t)) {
+ switch (index) {
+ case 0: result_type = t_u8_ptr; break;
+ case 1: result_type = t_int; break;
+ }
+ } else if (is_type_any(t)) {
+ switch (index) {
+ case 0: result_type = t_type_info_ptr; break;
+ case 1: result_type = t_rawptr; break;
+ }
+ } else if (is_type_dynamic_array(t)) {
+ switch (index) {
+ case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break;
+ case 1: result_type = t_int; break;
+ case 2: result_type = t_int; break;
+ case 3: result_type = t_allocator; break;
+ }
+ } else if (is_type_dynamic_map(t)) {
+ Type *gst = t->Map.generated_struct_type;
+ switch (index) {
+ case 0: result_type = gst->Record.fields[0]->type; break;
+ case 1: result_type = gst->Record.fields[1]->type; break;
+ }
+ } else {
+ GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s->type), index);
+ }
+
+ GB_ASSERT(result_type != NULL);
+
+ return ssa_new_value1i(p, ssaOp_ValueIndex, result_type, index, s);
+}
+
+
+ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel) {
+ GB_ASSERT(sel.index.count > 0);
+ Type *type = type_deref(e->type);
+
+ for_array(i, sel.index) {
+ i32 index = cast(i32)sel.index.e[i];
+ if (is_type_pointer(type)) {
+ type = type_deref(type);
+ e = ssa_emit_load(p, e);
+ }
+ type = base_type(type);
+
+
+ if (is_type_raw_union(type)) {
+ type = type->Record.fields[index]->type;
+ e = ssa_emit_conv(p, e, make_type_pointer(p->allocator, type));
+ } else if (type->kind == Type_Record) {
+ type = type->Record.fields[index]->type;
+ e = ssa_emit_ptr_index(p, e, index);
+ } else if (type->kind == Type_Tuple) {
+ type = type->Tuple.variables[index]->type;
+ e = ssa_emit_ptr_index(p, e, index);
+ }else if (type->kind == Type_Basic) {
+ switch (type->Basic.kind) {
+ case Basic_any: {
+ if (index == 0) {
+ type = t_type_info_ptr;
+ } else if (index == 1) {
+ type = t_rawptr;
+ }
+ e = ssa_emit_ptr_index(p, e, index);
+ } break;
+
+ case Basic_string:
+ e = ssa_emit_ptr_index(p, e, index);
+ break;
+
+ default:
+ GB_PANIC("un-gep-able type");
+ break;
+ }
+ } else if (type->kind == Type_Slice) {
+ e = ssa_emit_ptr_index(p, e, index);
+ } else if (type->kind == Type_DynamicArray) {
+ e = ssa_emit_ptr_index(p, e, index);
+ } else if (type->kind == Type_Vector) {
+ e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index));
+ } else if (type->kind == Type_Array) {
+ e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index));
+ } else if (type->kind == Type_Map) {
+ e = ssa_emit_ptr_index(p, e, 1);
+ switch (index) {
+ case 0: e = ssa_emit_ptr_index(p, e, 1); break; // count
+ case 1: e = ssa_emit_ptr_index(p, e, 2); break; // capacity
+ case 2: e = ssa_emit_ptr_index(p, e, 3); break; // allocator
+ }
+ } else {
+ GB_PANIC("un-gep-able type");
+ }
+ }
+
+ return e;
+}
+
+ssaValue *ssa_emit_deep_field_value_index(ssaProc *p, ssaValue *e, Selection sel) {
+ GB_ASSERT(sel.index.count > 0);
+ Type *type = e->type;
+ if (e->op == ssaOp_Load) {
+ if (!can_ssa_type(e->type)) {
+ ssaValue *ptr = ssa_emit_deep_field_ptr_index(p, e->args.e[0], sel);
+ return ssa_emit_load(p, ptr);
+ }
+ }
+ GB_ASSERT(can_ssa_type(e->type));
+
+ for_array(i, sel.index) {
+ i32 index = cast(i32)sel.index.e[i];
+ if (is_type_pointer(type)) {
+ e = ssa_emit_load(p, e);
+ }
+ type = base_type(type);
+
+
+ if (is_type_raw_union(type)) {
+ GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?");
+ type = type->Record.fields[index]->type;
+ e = ssa_emit_conv(p, e, type);
+ } else if (type->kind == Type_Map) {
+ e = ssa_emit_value_index(p, e, 1);
+ switch (index) {
+ case 0: e = ssa_emit_value_index(p, e, 1); break; // count
+ case 1: e = ssa_emit_value_index(p, e, 2); break; // capacity
+ case 2: e = ssa_emit_value_index(p, e, 3); break; // allocator
+ }
+ } else {
+ e = ssa_emit_value_index(p, e, index);
+ }
+ }
+
+ return e;
+}
+
+
+
+
+
ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) {
switch (expr->kind) {
case_ast_node(i, Ident, expr);
@@ -861,6 +942,71 @@ ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) {
case_ast_node(pe, ParenExpr, expr);
return ssa_build_addr(p, unparen_expr(expr));
case_end;
+
+ case_ast_node(se, SelectorExpr, expr);
+ ssa_emit_comment(p, str_lit("SelectorExpr"));
+ AstNode *sel = unparen_expr(se->selector);
+ if (sel->kind == AstNode_Ident) {
+ String selector = sel->Ident.string;
+ TypeAndValue *tav = type_and_value_of_expression(p->module->info, se->expr);
+
+ if (tav == NULL) {
+ // NOTE(bill): Imports
+ Entity *imp = entity_of_ident(p->module->info, se->expr);
+ if (imp != NULL) {
+ GB_ASSERT(imp->kind == Entity_ImportName);
+ }
+ return ssa_build_addr(p, se->selector);
+ }
+
+
+ Type *type = base_type(tav->type);
+ if (tav->mode == Addressing_Type) { // Addressing_Type
+ GB_PANIC("TODO: SelectorExpr Addressing_Type");
+ // Selection sel = lookup_field(p->allocator, type, selector, true);
+ // Entity *e = sel.entity;
+ // GB_ASSERT(e->kind == Entity_Variable);
+ // GB_ASSERT(e->flags & EntityFlag_TypeField);
+ // String name = e->token.string;
+ // if (str_eq(name, str_lit("names"))) {
+ // ssaValue *ti_ptr = ir_type_info(p, type);
+
+ // ssaValue *names_ptr = NULL;
+
+ // if (is_type_enum(type)) {
+ // ssaValue *enum_info = ssa_emit_conv(p, ti_ptr, t_type_info_enum_ptr);
+ // names_ptr = ssa_emit_ptr_index(p, enum_info, 1);
+ // } else if (type->kind == Type_Record) {
+ // ssaValue *record_info = ssa_emit_conv(p, ti_ptr, t_type_info_record_ptr);
+ // names_ptr = ssa_emit_ptr_index(p, record_info, 1);
+ // }
+ // return ssa_addr(names_ptr);
+ // } else {
+ // GB_PANIC("Unhandled TypeField %.*s", LIT(name));
+ // }
+ GB_PANIC("Unreachable");
+ }
+
+ Selection sel = lookup_field(p->allocator, type, selector, false);
+ GB_ASSERT(sel.entity != NULL);
+
+ ssaValue *a = ssa_build_addr(p, se->expr).addr;
+ a = ssa_emit_deep_field_ptr_index(p, a, sel);
+ return ssa_addr(a);
+ } else {
+ Type *type = base_type(type_of_expr(p->module->info, se->expr));
+ GB_ASSERT(is_type_integer(type));
+ ExactValue val = type_and_value_of_expression(p->module->info, sel)->value;
+ i64 index = val.value_integer;
+
+ Selection sel = lookup_field_from_index(p->allocator, type, index);
+ GB_ASSERT(sel.entity != NULL);
+
+ ssaValue *a = ssa_build_addr(p, se->expr).addr;
+ a = ssa_emit_deep_field_ptr_index(p, a, sel);
+ return ssa_addr(a);
+ }
+ case_end;
}
GB_PANIC("Cannot get entity's address");
@@ -1088,6 +1234,131 @@ ssaOp ssa_determine_op(TokenKind op, Type *t) {
return ssaOp_Invalid;
}
+ssaValue *ssa_emit_comp(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y) {
+ GB_ASSERT(x != NULL && y != NULL);
+ Type *a = core_type(x->type);
+ Type *b = core_type(y->type);
+ if (are_types_identical(a, b)) {
+ // NOTE(bill): No need for a conversion
+ } else if (ssa_is_op_const(x->op)) {
+ x = ssa_emit_conv(p, x, y->type);
+ } else if (ssa_is_op_const(y->op)) {
+ y = ssa_emit_conv(p, y, x->type);
+ }
+
+ Type *result = t_bool;
+ if (is_type_vector(a)) {
+ result = make_type_vector(p->allocator, t_bool, a->Vector.count);
+ }
+
+ if (is_type_vector(a)) {
+ ssa_emit_comment(p, str_lit("vector.comp.begin"));
+ Type *tl = base_type(a);
+ ssaValue *lhs = ssa_address_from_load_or_generate_local(p, x);
+ ssaValue *rhs = ssa_address_from_load_or_generate_local(p, y);
+
+ GB_ASSERT(is_type_vector(result));
+ Type *elem_type = base_type(result)->Vector.elem;
+
+ ssaAddr addr = ssa_add_local_generated(p, result);
+ for (i32 i = 0; i < tl->Vector.count; i++) {
+ ssaValue *index = ssa_const_int(p, t_int, i);
+ ssaValue *x = ssa_emit_load(p, ssa_emit_array_index(p, lhs, index));
+ ssaValue *y = ssa_emit_load(p, ssa_emit_array_index(p, rhs, index));
+ ssaValue *z = ssa_emit_comp(p, op, x, y);
+ ssa_emit_store(p, ssa_emit_array_index(p, addr.addr, index), z);
+ }
+
+ ssa_emit_comment(p, str_lit("vector.comp.end"));
+ return ssa_addr_load(p, addr);
+ }
+
+ return ssa_new_value2(p, ssa_determine_op(op, x->type), x->type, x, y);
+}
+
+
+
+ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) {
+ switch (cond->kind) {
+ case_ast_node(pe, ParenExpr, cond);
+ return ssa_build_cond(p, pe->expr, yes, no);
+ case_end;
+
+ case_ast_node(ue, UnaryExpr, cond);
+ if (ue->op.kind == Token_Not) {
+ return ssa_build_cond(p, ue->expr, no, yes);
+ }
+ case_end;
+
+ case_ast_node(be, BinaryExpr, cond);
+ if (be->op.kind == Token_CmpAnd) {
+ ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and");
+ ssa_build_cond(p, be->left, block, no);
+ ssa_start_block(p, block);
+ return ssa_build_cond(p, be->right, yes, no);
+ } else if (be->op.kind == Token_CmpOr) {
+ ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or");
+ ssa_build_cond(p, be->left, yes, block);
+ ssa_start_block(p, block);
+ return ssa_build_cond(p, be->right, yes, no);
+ }
+ case_end;
+ }
+
+ ssaValue *c = ssa_build_expr(p, cond);
+ ssaBlock *b = ssa_end_block(p);
+ b->kind = ssaBlock_If;
+ ssa_set_control(b, c);
+ ssa_add_edge_to(b, yes);
+ ssa_add_edge_to(b, no);
+ return c;
+}
+
+ssaValue *ssa_emit_logical_binary_expr(ssaProc *p, AstNode *expr) {
+ ast_node(be, BinaryExpr, expr);
+
+ ssaBlock *rhs = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.rhs");
+ ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.done");
+
+ GB_ASSERT(p->curr_block != NULL);
+
+ Type *type = default_type(type_of_expr(p->module->info, expr));
+
+ bool short_circuit_value = false;
+ if (be->op.kind == Token_CmpAnd) {
+ ssa_build_cond(p, be->left, rhs, done);
+ short_circuit_value = false;
+ } else if (be->op.kind == Token_CmpOr) {
+ ssa_build_cond(p, be->left, done, rhs);
+ short_circuit_value = true;
+ }
+ if (rhs->preds.count == 0) {
+ ssa_start_block(p, done);
+ return ssa_const_bool(p, type, short_circuit_value);
+ }
+
+ if (done->preds.count == 0) {
+ ssa_start_block(p, rhs);
+ return ssa_build_expr(p, be->right);
+ }
+
+ ssa_start_block(p, rhs);
+ ssaValue *short_circuit = ssa_const_bool(p, type, short_circuit_value);
+ ssaValueArgs edges = {0};
+ ssa_init_value_args(&edges, p->allocator);
+ for_array(i, done->preds) {
+ ssa_add_arg(&edges, short_circuit);
+ }
+
+ ssa_add_arg(&edges, ssa_build_expr(p, be->right));
+ ssa_emit_jump(p, done);
+ ssa_start_block(p, done);
+
+ ssaValue *phi = ssa_new_value0(p, ssaOp_Phi, type);
+ phi->args = edges;
+ return phi;
+}
+
ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
expr = unparen_expr(expr);
@@ -1106,7 +1377,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
} else if (is_type_integer(t)) {
GB_ASSERT(tv->value.kind == ExactValue_Integer);
- i64 s = 8*type_size_of(p->module->allocator, t);
+ i64 s = 8*type_size_of(p->allocator, t);
switch (s) {
case 8: return ssa_const_i8 (p, tv->type, tv->value.value_integer);
case 16: return ssa_const_i16(p, tv->type, tv->value.value_integer);
@@ -1116,7 +1387,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
}
} else if (is_type_float(t)) {
GB_ASSERT(tv->value.kind == ExactValue_Float);
- i64 s = 8*type_size_of(p->module->allocator, t);
+ i64 s = 8*type_size_of(p->allocator, t);
switch (s) {
case 32: return ssa_const_f32(p, tv->type, tv->value.value_float);
case 64: return ssa_const_f64(p, tv->type, tv->value.value_float);
@@ -1170,41 +1441,40 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
case_ast_node(ue, UnaryExpr, expr);
switch (ue->op.kind) {
case Token_Pointer: {
- ssaValue *ptr = ssa_build_addr(p, ue->expr).addr;
- return ssa_new_value1(p->curr_block, ssaOp_Copy, tv->type, ptr);
+ return ssa_build_addr(p, ue->expr).addr;
} break;
case Token_Add:
return ssa_build_expr(p, ue->expr);
case Token_Not: // Boolean not
- return ssa_new_value1(p->curr_block, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr));
+ return ssa_new_value1(p, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr));
case Token_Xor: { // Bitwise not
ssaValue *x = ssa_build_expr(p, ue->expr);
- isize bits = 8*type_size_of(p->module->allocator, x->type);
+ isize bits = 8*type_size_of(p->allocator, x->type);
switch (bits) {
- case 8: return ssa_new_value1(p->curr_block, ssaOp_Not8, tv->type, x);
- case 16: return ssa_new_value1(p->curr_block, ssaOp_Not16, tv->type, x);
- case 32: return ssa_new_value1(p->curr_block, ssaOp_Not32, tv->type, x);
- case 64: return ssa_new_value1(p->curr_block, ssaOp_Not64, tv->type, x);
+ case 8: return ssa_new_value1(p, ssaOp_Not8, tv->type, x);
+ case 16: return ssa_new_value1(p, ssaOp_Not16, tv->type, x);
+ case 32: return ssa_new_value1(p, ssaOp_Not32, tv->type, x);
+ case 64: return ssa_new_value1(p, ssaOp_Not64, tv->type, x);
}
GB_PANIC("unknown integer size");
} break;
case Token_Sub: { // 0-x
ssaValue *x = ssa_build_expr(p, ue->expr);
- isize bits = 8*type_size_of(p->module->allocator, x->type);
+ isize bits = 8*type_size_of(p->allocator, x->type);
if (is_type_integer(x->type)) {
switch (bits) {
- case 8: return ssa_new_value1(p->curr_block, ssaOp_Neg8, tv->type, x);
- case 16: return ssa_new_value1(p->curr_block, ssaOp_Neg16, tv->type, x);
- case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32, tv->type, x);
- case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64, tv->type, x);
+ case 8: return ssa_new_value1(p, ssaOp_Neg8, tv->type, x);
+ case 16: return ssa_new_value1(p, ssaOp_Neg16, tv->type, x);
+ case 32: return ssa_new_value1(p, ssaOp_Neg32, tv->type, x);
+ case 64: return ssa_new_value1(p, ssaOp_Neg64, tv->type, x);
}
} else if (is_type_float(x->type)) {
switch (bits) {
- case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32F, tv->type, x);
- case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64F, tv->type, x);
+ case 32: return ssa_new_value1(p, ssaOp_Neg32F, tv->type, x);
+ case 64: return ssa_new_value1(p, ssaOp_Neg64F, tv->type, x);
}
}
GB_PANIC("unknown type for -x");
@@ -1228,7 +1498,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
ssaValue *x = ssa_build_expr(p, be->left);
ssaValue *y = ssa_build_expr(p, be->right);
GB_ASSERT(x != NULL && y != NULL);
- return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y);
+ return ssa_new_value2(p, ssa_determine_op(be->op.kind, x->type), tv->type, x, y);
}
case Token_Shl:
@@ -1245,15 +1515,12 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
case Token_GtEq: {
ssaValue *x = ssa_build_expr(p, be->left);
ssaValue *y = ssa_build_expr(p, be->right);
- GB_ASSERT(x != NULL && y != NULL);
- return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y);
+ return ssa_emit_comp(p, be->op.kind, x, y);
} break;
case Token_CmpAnd:
case Token_CmpOr:
- GB_PANIC("TODO: inline && and ||");
- return NULL;
- // return ir_emit_logical_binary_expr(proc, expr);
+ return ssa_emit_logical_binary_expr(p, expr);
default:
GB_PANIC("Invalid binary expression");
@@ -1275,48 +1542,6 @@ void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes) {
}
-ssaValue *ssa_emit_struct_ep(ssaProc *p, ssaValue *ptr, i32 index) {
- GB_ASSERT(ptr->type != NULL);
- GB_ASSERT(is_type_pointer(ptr->type));
- return NULL;
-}
-
-
-ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) {
- switch (cond->kind) {
- case_ast_node(pe, ParenExpr, cond);
- return ssa_build_cond(p, pe->expr, yes, no);
- case_end;
-
- case_ast_node(ue, UnaryExpr, cond);
- if (ue->op.kind == Token_Not) {
- return ssa_build_cond(p, ue->expr, no, yes);
- }
- case_end;
-
- case_ast_node(be, BinaryExpr, cond);
- if (be->op.kind == Token_CmpAnd) {
- ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and");
- ssa_build_cond(p, be->left, block, no);
- ssa_start_block(p, block);
- return ssa_build_cond(p, be->right, yes, no);
- } else if (be->op.kind == Token_CmpOr) {
- ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or");
- ssa_build_cond(p, be->left, yes, block);
- ssa_start_block(p, block);
- return ssa_build_cond(p, be->right, yes, no);
- }
- case_end;
- }
-
- ssaValue *c = ssa_build_expr(p, cond);
- ssaBlock *b = ssa_end_block(p);
- b->kind = ssaBlock_If;
- ssa_set_control(b, c);
- ssa_add_edge_to(b, yes);
- ssa_add_edge_to(b, no);
- return c;
-}
void ssa_build_when_stmt(ssaProc *p, AstNodeWhenStmt *ws) {
ssaValue *cond = ssa_build_expr(p, ws->cond);
@@ -1370,9 +1595,11 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
case_end;
case_ast_node(us, UsingStmt, node);
- AstNode *decl = unparen_expr(us->node);
- if (decl->kind == AstNode_ValueDecl) {
- ssa_build_stmt(p, decl);
+ for_array(i, us->list) {
+ AstNode *decl = unparen_expr(us->list.e[i]);
+ if (decl->kind == AstNode_ValueDecl) {
+ ssa_build_stmt(p, decl);
+ }
}
case_end;
@@ -1426,7 +1653,7 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
for (isize i = 0; i < t->Tuple.variable_count; i++) {
Entity *e = t->Tuple.variables[i];
- ssaValue *v = ssa_emit_struct_ep(p, init, i);
+ ssaValue *v = ssa_emit_ptr_index(p, init, i);
array_add(&inits, v);
}
} else {
@@ -1492,7 +1719,7 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
for (isize i = 0; i < t->Tuple.variable_count; i++) {
Entity *e = t->Tuple.variables[i];
- ssaValue *v = ssa_emit_struct_ep(p, init, i);
+ ssaValue *v = ssa_emit_ptr_index(p, init, i);
array_add(&inits, v);
}
} else {
@@ -1839,6 +2066,9 @@ void ssa_print_proc(gbFile *f, ssaProc *p) {
}
+void ssa_opt_proc(ssaProc *p) {
+}
+
void ssa_build_proc(ssaModule *m, ssaProc *p) {
p->module = m;
m->proc = p;
@@ -1860,6 +2090,8 @@ void ssa_build_proc(ssaModule *m, ssaProc *p) {
p->exit = ssa_new_block(p, ssaBlock_Exit, "exit");
ssa_emit_jump(p, p->exit);
+ ssa_opt_proc(p);
+
ssa_print_proc(gb_file_get_standard(gbFileStandard_Error), p);
}
diff --git a/src/ssa_op.c b/src/ssa_op.c
new file mode 100644
index 000000000..22bde663d
--- /dev/null
+++ b/src/ssa_op.c
@@ -0,0 +1,277 @@
+#define SSA_OPS \
+ SSA_OP(Invalid)\
+\
+ SSA_OP(Unknown)\
+\
+ SSA_OP(Comment) /* Does nothing */\
+\
+ SSA_OP(SP) /* Stack Pointer */\
+ SSA_OP(SB) /* Stack Base */\
+ SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\
+\
+ SSA_OP(Local)\
+ SSA_OP(Global)\
+ SSA_OP(Proc)\
+\
+ SSA_OP(Load)\
+ SSA_OP(Store)\
+ SSA_OP(Move)\
+ SSA_OP(LoadReg)\
+ SSA_OP(StoreReg)\
+ SSA_OP(Zero) /* Zero initialize */\
+\
+ SSA_OP(ArrayIndex) /* Index for a fixed array */\
+ SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
+ SSA_OP(PtrOffset)\
+ SSA_OP(ValueIndex) /* Extract for a value from a register */\
+\
+ SSA_OP(Phi)\
+ SSA_OP(Copy)\
+\
+ /* TODO(bill): calling conventions */\
+ SSA_OP(CallOdin)\
+ SSA_OP(CallC)\
+ SSA_OP(CallStd)\
+ SSA_OP(CallFast)\
+\
+ SSA_OP(BoundsCheck)\
+ SSA_OP(SliceBoundsCheck)\
+\
+ /* Built in operations/procedures */\
+ SSA_OP(Bswap16)\
+ SSA_OP(Bswap32)\
+ SSA_OP(Bswap64)\
+\
+ SSA_OP(Assume)\
+ SSA_OP(DebugTrap)\
+ SSA_OP(Trap)\
+ SSA_OP(ReadCycleCounter)\
+\
+\
+ SSA_OP(ConstBool)\
+ SSA_OP(ConstString)\
+ SSA_OP(ConstSlice)\
+ SSA_OP(ConstNil)\
+ SSA_OP(Const8)\
+ SSA_OP(Const16)\
+ SSA_OP(Const32)\
+ SSA_OP(Const64)\
+ SSA_OP(Const32F)\
+ SSA_OP(Const64F)\
+\
+ /* These should be all the operations I could possibly need for the mean time */\
+ SSA_OP(Add8)\
+ SSA_OP(Add16)\
+ SSA_OP(Add32)\
+ SSA_OP(Add64)\
+ SSA_OP(AddPtr)\
+ SSA_OP(Add32F)\
+ SSA_OP(Add64F)\
+ SSA_OP(Sub8)\
+ SSA_OP(Sub16)\
+ SSA_OP(Sub32)\
+ SSA_OP(Sub64)\
+ SSA_OP(SubPtr)\
+ SSA_OP(Sub32F)\
+ SSA_OP(Sub64F)\
+ SSA_OP(Mul8)\
+ SSA_OP(Mul16)\
+ SSA_OP(Mul32)\
+ SSA_OP(Mul64)\
+ SSA_OP(Mul32F)\
+ SSA_OP(Mul64F)\
+ SSA_OP(Div8)\
+ SSA_OP(Div8U)\
+ SSA_OP(Div16)\
+ SSA_OP(Div16U)\
+ SSA_OP(Div32)\
+ SSA_OP(Div32U)\
+ SSA_OP(Div64)\
+ SSA_OP(Div64U)\
+ SSA_OP(Div32F)\
+ SSA_OP(Div64F)\
+ SSA_OP(Mod8)\
+ SSA_OP(Mod8U)\
+ SSA_OP(Mod16)\
+ SSA_OP(Mod16U)\
+ SSA_OP(Mod32)\
+ SSA_OP(Mod32U)\
+ SSA_OP(Mod64)\
+ SSA_OP(Mod64U)\
+\
+ SSA_OP(And8)\
+ SSA_OP(And16)\
+ SSA_OP(And32)\
+ SSA_OP(And64)\
+ SSA_OP(Or8)\
+ SSA_OP(Or16)\
+ SSA_OP(Or32)\
+ SSA_OP(Or64)\
+ SSA_OP(Xor8)\
+ SSA_OP(Xor16)\
+ SSA_OP(Xor32)\
+ SSA_OP(Xor64)\
+ SSA_OP(AndNot8)\
+ SSA_OP(AndNot16)\
+ SSA_OP(AndNot32)\
+ SSA_OP(AndNot64)\
+\
+ SSA_OP(Lsh8x8)\
+ SSA_OP(Lsh8x16)\
+ SSA_OP(Lsh8x32)\
+ SSA_OP(Lsh8x64)\
+ SSA_OP(Lsh16x8)\
+ SSA_OP(Lsh16x16)\
+ SSA_OP(Lsh16x32)\
+ SSA_OP(Lsh16x64)\
+ SSA_OP(Lsh32x8)\
+ SSA_OP(Lsh32x16)\
+ SSA_OP(Lsh32x32)\
+ SSA_OP(Lsh32x64)\
+ SSA_OP(Lsh64x8)\
+ SSA_OP(Lsh64x16)\
+ SSA_OP(Lsh64x32)\
+ SSA_OP(Lsh64x64)\
+ SSA_OP(Rsh8x8)\
+ SSA_OP(Rsh8x16)\
+ SSA_OP(Rsh8x32)\
+ SSA_OP(Rsh8x64)\
+ SSA_OP(Rsh16x8)\
+ SSA_OP(Rsh16x16)\
+ SSA_OP(Rsh16x32)\
+ SSA_OP(Rsh16x64)\
+ SSA_OP(Rsh32x8)\
+ SSA_OP(Rsh32x16)\
+ SSA_OP(Rsh32x32)\
+ SSA_OP(Rsh32x64)\
+ SSA_OP(Rsh64x8)\
+ SSA_OP(Rsh64x16)\
+ SSA_OP(Rsh64x32)\
+ SSA_OP(Rsh64x64)\
+ SSA_OP(Rsh8Ux8)\
+ SSA_OP(Rsh8Ux16)\
+ SSA_OP(Rsh8Ux32)\
+ SSA_OP(Rsh8Ux64)\
+ SSA_OP(Rsh16Ux8)\
+ SSA_OP(Rsh16Ux16)\
+ SSA_OP(Rsh16Ux32)\
+ SSA_OP(Rsh16Ux64)\
+ SSA_OP(Rsh32Ux8)\
+ SSA_OP(Rsh32Ux16)\
+ SSA_OP(Rsh32Ux32)\
+ SSA_OP(Rsh32Ux64)\
+ SSA_OP(Rsh64Ux8)\
+ SSA_OP(Rsh64Ux16)\
+ SSA_OP(Rsh64Ux32)\
+ SSA_OP(Rsh64Ux64)\
+\
+ SSA_OP(Eq8)\
+ SSA_OP(Eq16)\
+ SSA_OP(Eq32)\
+ SSA_OP(Eq64)\
+ SSA_OP(EqPtr)\
+ SSA_OP(Eq32F)\
+ SSA_OP(Eq64F)\
+ SSA_OP(Ne8)\
+ SSA_OP(Ne16)\
+ SSA_OP(Ne32)\
+ SSA_OP(Ne64)\
+ SSA_OP(NePtr)\
+ SSA_OP(Ne32F)\
+ SSA_OP(Ne64F)\
+ SSA_OP(Lt8)\
+ SSA_OP(Lt16)\
+ SSA_OP(Lt32)\
+ SSA_OP(Lt64)\
+ SSA_OP(LtPtr)\
+ SSA_OP(Lt32F)\
+ SSA_OP(Lt64F)\
+ SSA_OP(Gt8)\
+ SSA_OP(Gt16)\
+ SSA_OP(Gt32)\
+ SSA_OP(Gt64)\
+ SSA_OP(GtPtr)\
+ SSA_OP(Gt32F)\
+ SSA_OP(Gt64F)\
+ SSA_OP(Le8)\
+ SSA_OP(Le16)\
+ SSA_OP(Le32)\
+ SSA_OP(Le64)\
+ SSA_OP(LePtr)\
+ SSA_OP(Le32F)\
+ SSA_OP(Le64F)\
+ SSA_OP(Ge8)\
+ SSA_OP(Ge16)\
+ SSA_OP(Ge32)\
+ SSA_OP(Ge64)\
+ SSA_OP(GePtr)\
+ SSA_OP(Ge32F)\
+ SSA_OP(Ge64F)\
+\
+ SSA_OP(NotB)\
+ SSA_OP(EqB)\
+ SSA_OP(NeB)\
+\
+ SSA_OP(Neg8)\
+ SSA_OP(Neg16)\
+ SSA_OP(Neg32)\
+ SSA_OP(Neg64)\
+ SSA_OP(Neg32F)\
+ SSA_OP(Neg64F)\
+\
+ SSA_OP(Not8)\
+ SSA_OP(Not16)\
+ SSA_OP(Not32)\
+ SSA_OP(Not64)\
+\
+ SSA_OP(SignExt8to16)\
+ SSA_OP(SignExt8to32)\
+ SSA_OP(SignExt8to64)\
+ SSA_OP(SignExt16to32)\
+ SSA_OP(SignExt16to64)\
+ SSA_OP(SignExt32to64)\
+ SSA_OP(ZeroExt8to16)\
+ SSA_OP(ZeroExt8to32)\
+ SSA_OP(ZeroExt8to64)\
+ SSA_OP(ZeroExt16to32)\
+ SSA_OP(ZeroExt16to64)\
+ SSA_OP(ZeroExt32to64)\
+ SSA_OP(Trunc16to8)\
+ SSA_OP(Trunc32to8)\
+ SSA_OP(Trunc32to16)\
+ SSA_OP(Trunc64to8)\
+ SSA_OP(Trunc64to16)\
+ SSA_OP(Trunc64to32)\
+\
+ SSA_OP(Cvt32to32F)\
+ SSA_OP(Cvt32to64F)\
+ SSA_OP(Cvt64to32F)\
+ SSA_OP(Cvt64to64F)\
+ SSA_OP(Cvt32Fto32)\
+ SSA_OP(Cvt32Fto64)\
+ SSA_OP(Cvt64Fto32)\
+ SSA_OP(Cvt64Fto64)\
+ SSA_OP(Cvt32Fto64F)\
+ SSA_OP(Cvt64Fto32F)\
+ SSA_OP(Cvt32Uto32F)\
+ SSA_OP(Cvt32Uto64F)\
+ SSA_OP(Cvt32Fto32U)\
+ SSA_OP(Cvt64Fto32U)\
+ SSA_OP(Cvt64Uto32F)\
+ SSA_OP(Cvt64Uto64F)\
+ SSA_OP(Cvt32Fto64U)\
+ SSA_OP(Cvt64Fto64U)\
+
+
+enum ssaOp {
+#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
+ SSA_OPS
+#undef SSA_OP
+};
+typedef enum ssaOp ssaOp;
+
+String const ssa_op_strings[] = {
+#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
+ SSA_OPS
+#undef SSA_OP
+};
diff --git a/src/tokenizer.c b/src/tokenizer.c
index cfc1423f0..6c7f76c02 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -484,15 +484,9 @@ gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible
}
-gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
- if (allow_underscore) {
- while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
- advance_to_next_rune(t);
- }
- } else {
- while (digit_value(t->curr_rune) < base) {
- advance_to_next_rune(t);
- }
+gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
+ while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
+ advance_to_next_rune(t);
}
}
@@ -506,7 +500,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
if (seen_decimal_point) {
token.kind = Token_Float;
- scan_mantissa(t, 10, true);
+ scan_mantissa(t, 10);
goto exponent;
}
@@ -515,31 +509,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t);
- scan_mantissa(t, 2, true);
+ scan_mantissa(t, 2);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'o') { // Octal
advance_to_next_rune(t);
- scan_mantissa(t, 8, true);
+ scan_mantissa(t, 8);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'd') { // Decimal
advance_to_next_rune(t);
- scan_mantissa(t, 10, true);
+ scan_mantissa(t, 10);
+ if (t->curr - prev <= 2) {
+ token.kind = Token_Invalid;
+ }
+ } else if (t->curr_rune == 'z') { // Dozenal
+ advance_to_next_rune(t);
+ scan_mantissa(t, 12);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'x') { // Hexadecimal
advance_to_next_rune(t);
- scan_mantissa(t, 16, true);
+ scan_mantissa(t, 16);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else {
seen_decimal_point = false;
- scan_mantissa(t, 10, true);
+ scan_mantissa(t, 10);
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true;
@@ -551,7 +551,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
return token;
}
- scan_mantissa(t, 10, true);
+ scan_mantissa(t, 10);
fraction:
if (t->curr_rune == '.') {
@@ -564,7 +564,7 @@ fraction:
goto end;
}
token.kind = Token_Float;
- scan_mantissa(t, 10, true);
+ scan_mantissa(t, 10);
}
exponent:
@@ -574,7 +574,7 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t);
}
- scan_mantissa(t, 10, false);
+ scan_mantissa(t, 10);
}
end: