Compare commits

..

17 Commits

Author SHA1 Message Date
gingerBill 784c48c9e3 Redefine how union tag size is calculated to match alignment of the union 2018-11-29 23:00:16 +00:00
gingerBill 008d8f25c8 Fix assertion on union assignment in compound literal 2018-11-29 22:50:08 +00:00
gingerBill 7ffcf34dca Modify how custom alignment is printed for LLVM IR 2018-11-29 22:47:08 +00:00
gingerBill f3a4904f21 Hack: union compound literal fix 2018-11-29 22:23:30 +00:00
gingerBill 3aec78b1d4 Lock on possible race condition in parser 2018-11-29 20:27:48 +00:00
gingerBill a3e6e8d304 Allow single field struct #raw_union 2018-11-29 19:46:45 +00:00
gingerBill a747c03f29 Fix #complete switch on pointers to unions #286 2018-11-29 18:36:45 +00:00
gingerBill 2301ae157c Fix recursive loop bug for is_type_polymorphic 2018-11-28 16:47:20 +00:00
gingerBill d3c7d6d485 Fix #284 2018-11-26 10:55:15 +00:00
gingerBill 9b063ad9a3 Fix poly proc determination by cloning the signature node 2018-11-25 17:57:49 +00:00
gingerBill c2f9bf489e Fix debug information for entities without an associated identifier 2018-11-25 17:31:53 +00:00
gingerBill e496b95881 Subset and superset operators for bit_set: < <= > >= 2018-11-25 16:19:17 +00:00
gingerBill 444f4f446a -vet flag to do basic vetting of code 2018-11-25 14:14:58 +00:00
gingerBill 41ad896f3f Update README.md 2018-11-25 11:21:11 +00:00
gingerBill 0a4b88f9a6 Fix Issue with referencing a polymorphic struct in another package referencing itself #283 2018-11-25 10:35:49 +00:00
gingerBill 4c2f03b1f2 Fix compile time bounds check test 2018-11-23 10:38:17 +00:00
gingerBill 52dcaeb1e9 Fix transmute with cstring and integers 2018-11-22 20:59:24 +00:00
19 changed files with 451 additions and 89 deletions
+2
View File
@@ -72,6 +72,8 @@ main :: proc() {
## Requirements to build and run
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
- Windows
* x86-64
* MSVC 2010 installed (C++11 support)
+3 -4
View File
@@ -843,7 +843,6 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
case runtime.Type_Info_Bit_Set:
bits: u64;
bit_size := u64(8*type_info.size);
verb := 'b';
switch bit_size {
case 0: bits = 0;
@@ -886,7 +885,7 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
}
}
}
fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, bit_field_name: string = "") {
type_info := type_info_of(v.id);
switch info in type_info.variant {
case runtime.Type_Info_Named:
@@ -902,8 +901,8 @@ fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
case 8: data = cast(u64)(^u64)(v.data)^;
}
if name != "" {
write_string(fi.buf, name);
if bit_field_name != "" {
write_string(fi.buf, bit_field_name);
write_byte(fi.buf, '{');
} else {
write_string(fi.buf, "bit_field{");
+2 -2
View File
@@ -226,9 +226,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case Allocator_Mode.Free:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr {
size := scratch.curr_offset - scratch.prev_offset;
full_size := scratch.curr_offset - scratch.prev_offset;
scratch.curr_offset = scratch.prev_offset;
zero(last_ptr, size);
zero(last_ptr, full_size);
return nil;
}
// NOTE(bill): It's scratch memory, don't worry about freeing
+34 -5
View File
@@ -67,12 +67,41 @@ copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawpt
compare :: proc "contextless" (a, b: []byte) -> int {
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
}
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int {
pa :: ptr_offset;
for i in 0..n-1 do switch {
case pa(a, i)^ < pa(b, i)^: return -1;
case pa(a, i)^ > pa(b, i)^: return +1;
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
ptr_idx :: inline proc(ptr: $P/^$T, n: int) -> T {
return ptr_offset(ptr, n)^;
}
x := slice_ptr(a, n);
y := slice_ptr(b, n);
SU :: size_of(uintptr);
fast := n/SU + 1;
offset := (fast-1)*SU;
curr_block := 0;
if n < SU {
fast = 0;
}
la := slice_ptr((^uintptr)(a), fast);
lb := slice_ptr((^uintptr)(b), fast);
for /**/; curr_block < fast; curr_block += 1 {
if la[curr_block] ~ lb[curr_block] != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
if x[pos] ~ y[pos] != 0 {
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
if x[offset] ~ y[offset] != 0 {
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1;
}
}
return 0;
}
+1 -1
View File
@@ -22,13 +22,13 @@ print_u64 :: proc(fd: os.Handle, u: u64) {
print_i64 :: proc(fd: os.Handle, u: i64) {
digits := "0123456789";
b :: i64(10);
neg := u < 0;
u = abs(u);
a: [129]byte;
i := len(a);
b := i64(10);
for u >= b {
i -= 1; a[i] = digits[u % b];
u /= b;
+15 -1
View File
@@ -408,7 +408,6 @@ parametric_polymorphism :: proc() {
}
assert(table.count <= len(table.slots));
hash := get_hash(key);
index = int(hash % u32(len(table.slots)));
for table.slots[index].occupied {
@@ -815,6 +814,21 @@ bit_set_type :: proc() {
y |= {1, 4, 2};
assert(2 in y);
}
{
Letters :: bit_set['A'..'Z'];
a := Letters{'A', 'B'};
b := Letters{'A', 'B', 'C', 'D', 'F'};
c := Letters{'A', 'B'};
assert(a <= b); // 'a' is a subset of 'b'
assert(b >= a); // 'b' is a superset of 'a'
assert(a < b); // 'a' is a strict subset of 'b'
assert(b > a); // 'b' is a strict superset of 'a'
assert(!(a < c)); // 'a' is a not strict subset of 'c'
assert(!(c > a)); // 'c' is a not strict superset of 'a'
}
}
diverging_procedures :: proc() {
+1
View File
@@ -103,6 +103,7 @@ struct BuildContext {
bool no_output_files;
bool no_crt;
bool use_lld;
bool vet;
gbAffinity affinity;
isize thread_count;
+59 -17
View File
@@ -27,7 +27,7 @@ struct CallArgumentData {
};
struct PolyProcData {
Entity * gen_entity;
Entity * gen_entity;
ProcInfo proc_info;
};
@@ -278,7 +278,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
return false;
}
auto *found_gen_procs = map_get(&nctx.info->gen_procs, hash_pointer(base_entity->identifier));
if (found_gen_procs) {
auto procs = *found_gen_procs;
@@ -304,13 +303,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
// NOTE(bill): Reset scope from the failed procedure type
scope_reset(scope);
success = check_procedure_type(&nctx, final_proc_type, pt->node, &operands);
// LEAK TODO(bill): Cloning this AST may be leaky
Ast *cloned_proc_type_node = clone_ast(pt->node);
success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
if (!success) {
return false;
}
if (found_gen_procs) {
auto procs = *found_gen_procs;
for_array(i, procs) {
@@ -1558,7 +1558,11 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
case Token_Gt:
case Token_LtEq:
case Token_GtEq:
defined = is_type_ordered(x->type) && is_type_ordered(y->type);
if (are_types_identical(x->type, y->type) && is_type_bit_set(x->type)) {
defined = true;
} else {
defined = is_type_ordered(x->type) && is_type_ordered(y->type);
}
break;
}
@@ -1596,7 +1600,42 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
if (x->mode == Addressing_Constant &&
y->mode == Addressing_Constant) {
if (is_type_constant_type(x->type)) {
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
if (is_type_bit_set(x->type)) {
switch (op) {
case Token_CmpEq:
case Token_NotEq:
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
break;
case Token_Lt:
case Token_LtEq:
{
ExactValue lhs = x->value;
ExactValue rhs = y->value;
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
res = exact_value_bool(compare_exact_values(op, res, lhs));
if (op == Token_Lt) {
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
}
x->value = res;
break;
}
case Token_Gt:
case Token_GtEq:
{
ExactValue lhs = x->value;
ExactValue rhs = y->value;
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
res = exact_value_bool(compare_exact_values(op, res, rhs));
if (op == Token_Gt) {
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
}
x->value = res;
break;
}
}
} else {
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
}
} else {
x->mode = Addressing_Value;
}
@@ -2084,8 +2123,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
add_package_dependency(c, "runtime", "__dynamic_map_get");
} else if (is_type_bit_set(y->type)) {
Type *yt = base_type(y->type);
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
if (x->mode == Addressing_Constant && y->mode == Addressing_Constant) {
ExactValue k = exact_value_to_integer(x->value);
ExactValue v = exact_value_to_integer(y->value);
@@ -2109,7 +2148,6 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
x->mode = Addressing_Invalid;
}
}
} else {
gbString t = type_to_string(y->type);
error(x->expr, "expected either a map or bitset for 'in', got %s", t);
@@ -2647,9 +2685,9 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
if (value) *value = v;
bool out_of_bounds = false;
if (open_range) {
out_of_bounds = v > max_count;
out_of_bounds = v > max_count+1;
} else {
out_of_bounds = v >= max_count;
out_of_bounds = v > max_count;
}
if (v < 0) {
out_of_bounds = true;
@@ -4818,7 +4856,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
ast_node(ce, CallExpr, call);
Type *original_type = operand->type;
Type *struct_type = base_type(operand->type);
GB_ASSERT(is_type_polymorphic_record(original_type));
bool show_error = true;
@@ -4983,6 +5020,11 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
String generated_name = make_string_c(expr_to_string(call));
CheckerContext ctx = *c;
// NOTE(bill): We need to make sure the lookup scope for the record is the same as where it was created
ctx.scope = polymorphic_record_parent_scope(original_type);
GB_ASSERT(ctx.scope != nullptr);
Type *named_type = alloc_type_named(generated_name, nullptr, nullptr);
Type *bt = base_type(original_type);
if (bt->kind == Type_Struct) {
@@ -4992,9 +5034,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
struct_type->Struct.polymorphic_parent = original_type;
set_base_type(named_type, struct_type);
check_open_scope(c, node);
check_struct_type(c, struct_type, node, &ordered_operands, named_type, original_type);
check_close_scope(c);
check_open_scope(&ctx, node);
check_struct_type(&ctx, struct_type, node, &ordered_operands, named_type, original_type);
check_close_scope(&ctx);
} else if (bt->kind == Type_Union) {
Ast *node = clone_ast(bt->Union.node);
Type *union_type = alloc_type_union();
@@ -5002,9 +5044,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
union_type->Union.polymorphic_parent = original_type;
set_base_type(named_type, union_type);
check_open_scope(c, node);
check_union_type(c, union_type, node, &ordered_operands, named_type, original_type);
check_close_scope(c);
check_open_scope(&ctx, node);
check_union_type(&ctx, union_type, node, &ordered_operands, named_type, original_type);
check_close_scope(&ctx);
} else {
GB_PANIC("Unsupported parametric polymorphic record type");
}
+1 -1
View File
@@ -1008,7 +1008,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
if (complete) {
Type *ut = base_type(x.type);
Type *ut = base_type(type_deref(x.type));
GB_ASSERT(is_type_union(ut));
auto variants = ut->Union.variants;
+10 -1
View File
@@ -333,7 +333,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
scope_reserve(ctx->scope, min_field_count);
if (st->is_raw_union) {
if (st->is_raw_union && min_field_count > 1) {
struct_type->Struct.is_raw_union = true;
context = str_lit("struct #raw_union");
}
@@ -1566,6 +1566,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx->checker, scope, name, param);
if (is_using) {
add_entity_use(ctx, name, param);
}
array_add(&variables, param);
}
}
@@ -2521,6 +2524,12 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
type->Named.base = t_invalid;
}
if (is_type_polymorphic(type)) {
type->flags |= TypeFlag_Polymorphic;
} else if (is_type_polymorphic(type, true)) {
type->flags |= TypeFlag_PolySpecialized;
}
#if 0
if (!ctx->allow_polymorphic_types && is_type_polymorphic(type)) {
gbString str = type_to_string(type);
+140 -19
View File
@@ -320,6 +320,8 @@ void check_open_scope(CheckerContext *c, Ast *node) {
case Ast_StructType:
case Ast_EnumType:
case Ast_UnionType:
case Ast_BitFieldType:
case Ast_BitSetType:
scope->flags |= ScopeFlag_Type;
break;
}
@@ -413,41 +415,160 @@ GB_COMPARE_PROC(entity_variable_pos_cmp) {
}
enum VettedEntityKind {
VettedEntity_Invalid,
VettedEntity_Unused,
VettedEntity_Shadowed,
};
struct VettedEntity {
VettedEntityKind kind;
Entity *entity;
Entity *other;
};
void init_vetted_entity(VettedEntity *ve, VettedEntityKind kind, Entity *entity, Entity *other=nullptr) {
ve->kind = kind;
ve->entity = entity;
ve->other = other;
}
GB_COMPARE_PROC(vetted_entity_variable_pos_cmp) {
Entity *x = (*cast(VettedEntity **)a)->entity;
Entity *y = (*cast(VettedEntity **)b)->entity;
return token_pos_cmp(x->token.pos, y->token.pos);
}
bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
if (e->kind != Entity_Variable) {
return false;
}
String name = e->token.string;
if (name == "_") {
return false;
}
if (e->flags & EntityFlag_Param) {
return false;
}
if (e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Proc)) {
return false;
}
Scope *parent = e->scope->parent;
if (parent->flags & (ScopeFlag_Global|ScopeFlag_File)) {
return false;
}
Entity *shadowed = scope_lookup(parent, name);
if (shadowed == nullptr) {
return false;
}
if (shadowed->kind != Entity_Variable) {
return false;
}
if (shadowed->scope->flags & (ScopeFlag_Global|ScopeFlag_File)) {
// return false;
}
// NOTE(bill): The entities must be in the same file
if (e->token.pos.file != shadowed->token.pos.file) {
return false;
}
// NOTE(bill): The shaded identifier must appear before this one to be an
// instance of shadowing
if (token_pos_cmp(shadowed->token.pos, e->token.pos) > 0) {
return false;
}
// NOTE(bill): If the types differ, don't complain
if (are_types_identical(e->type, shadowed->type)) {
gb_zero_item(ve);
ve->kind = VettedEntity_Shadowed;
ve->entity = e;
ve->other = shadowed;
return true;
}
return false;
}
bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
if ((e->flags&EntityFlag_Used) == 0) {
switch (e->kind) {
case Entity_Variable:
case Entity_ImportName:
case Entity_LibraryName:
gb_zero_item(ve);
ve->kind = VettedEntity_Unused;
ve->entity = e;
return true;
}
}
return false;
}
void check_scope_usage(Checker *c, Scope *scope) {
// TODO(bill): Use this?
#if 0
Array<Entity *> unused = {};
array_init(&unused, heap_allocator());
defer (array_free(&unused));
if (!build_context.vet) {
return;
}
bool vet_unused = true;
bool vet_shadowing = true;
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
if (e != nullptr && (e->flags&EntityFlag_Used) == 0) {
switch (e->kind) {
case Entity_Variable:
case Entity_ImportName:
case Entity_LibraryName:
array_add(&unused, e);
break;
}
if (e == nullptr) continue;
VettedEntity ve = {};
if (vet_unused && check_vet_unused(c, e, &ve)) {
array_add(&vetted_entities, ve);
}
if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
array_add(&vetted_entities, ve);
}
}
gb_sort_array(unused.data, unused.count, entity_variable_pos_cmp);
gb_sort_array(vetted_entities.data, vetted_entities.count, vetted_entity_variable_pos_cmp);
for_array(i, unused) {
Entity *e = unused[i];
error(e->token, "'%.*s' declared but not used", LIT(e->token.string));
for_array(i, vetted_entities) {
auto ve = vetted_entities[i];
Entity *e = ve.entity;
Entity *other = ve.other;
String name = e->token.string;
switch (ve.kind) {
case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name));
break;
case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) {
error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
} else {
error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
}
break;
default:
break;
}
}
array_free(&vetted_entities);
for (Scope *child = scope->first_child;
child != nullptr;
child = child->next) {
if (!child->is_proc && !child->is_struct && !child->is_file) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
// Ignore these
} else {
check_scope_usage(c, child);
}
}
#endif
}
-1
View File
@@ -349,7 +349,6 @@ enum ScopeFlag {
ScopeFlag_Proc = 1<<5,
ScopeFlag_Type = 1<<6,
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
};
+4 -4
View File
@@ -85,14 +85,14 @@ struct Entity {
Token token;
Scope * scope;
Type * type;
Ast * identifier; // Can be nullptr
Ast * identifier; // Can be nullptr
DeclInfo * decl_info;
DeclInfo * parent_proc_decl; // nullptr if in file/global scope
AstPackage *pkg;
// TODO(bill): Cleanup how `using` works for entities
Entity * using_parent;
Ast * using_expr;
Ast * using_expr;
isize order_in_src;
String deprecated_message;
@@ -109,7 +109,7 @@ struct Entity {
String thread_local_model;
Entity * foreign_library;
Ast * foreign_library_ident;
Ast * foreign_library_ident;
String link_name;
String link_prefix;
bool is_foreign;
@@ -117,9 +117,9 @@ struct Entity {
bool is_immutable;
} Variable;
struct {
bool is_type_alias;
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
} TypeName;
struct {
u64 tags;
+9 -6
View File
@@ -763,7 +763,7 @@ extern "C++" {
#ifndef GB_ASSERT_MSG
#define GB_ASSERT_MSG(cond, msg, ...) do { \
if (!(cond)) { \
gb_assert_handler(#cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
gb_assert_handler("Assertion Failure", #cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
GB_DEBUG_TRAP(); \
} \
} while (0)
@@ -779,10 +779,13 @@ extern "C++" {
// NOTE(bill): Things that shouldn't happen with a message!
#ifndef GB_PANIC
#define GB_PANIC(msg, ...) GB_ASSERT_MSG(0, msg, ##__VA_ARGS__)
#define GB_PANIC(msg, ...) do { \
gb_assert_handler("Panic", NULL, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
GB_DEBUG_TRAP(); \
} while (0)
#endif
GB_DEF void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...);
GB_DEF void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...);
@@ -979,7 +982,7 @@ typedef struct gbThread {
gbSemaphore semaphore;
isize stack_size;
b32 is_running;
b32 volatile is_running;
} gbThread;
GB_DEF void gb_thread_init (gbThread *t);
@@ -3613,8 +3616,8 @@ extern "C" {
#pragma warning(disable:4127) // Conditional expression is constant
#endif
void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...) {
gb_printf_err("%s(%d): Assert Failure: ", file, line);
void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...) {
gb_printf_err("%s(%d): %s: ", file, line, prefix);
if (condition)
gb_printf_err( "`%s` ", condition);
if (msg) {
+83 -15
View File
@@ -846,7 +846,7 @@ void ir_build_proc (irValue *value, irProcedure *parent);
void ir_gen_global_type_name(irModule *m, Entity *e, String name);
irValue *ir_get_type_info_ptr (irProcedure *proc, Type *type);
void ir_value_set_debug_location(irProcedure *proc, irValue *v);
void ir_push_debug_location (irModule *m, Ast *node, irDebugInfo *scope);
void ir_push_debug_location (irModule *m, Ast *node, irDebugInfo *scope, Entity *e=nullptr);
void ir_pop_debug_location (irModule *m);
irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id);
irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file);
@@ -1513,7 +1513,8 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
}
// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
if (proc->module->generate_debug_info && proc->entity != nullptr) {
// if (proc->module->generate_debug_info && proc->entity != nullptr) {
if (proc->module->generate_debug_info) {
// GB_ASSERT_NOT_NULL(proc->debug_scope);
if (expr != nullptr) {
ir_emit(proc, ir_instr_debug_declare(proc, expr, e, true, instr));
@@ -1589,7 +1590,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i
irValue *v = ir_value_param(proc, e, abi_type);
irValueParam *p = &v->Param;
ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope);
ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e);
defer (ir_pop_debug_location(proc->module));
switch (p->kind) {
@@ -2590,8 +2591,21 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc) {
return di;
}
irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *scope) {
irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *scope, Entity *e) {
if (node == nullptr || scope == nullptr) {
if (e != nullptr && scope != nullptr) {
// irDebugInfo **existing = map_get(&m->debug_info, hash_entity(e));
// if (existing != nullptr) {
// return *existing;
// }
// // TODO HACK(bill): This is a little dirty but it is should do for the weird edge cases
// irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Location);
// di->Location.pos = e->token.pos;
// di->Location.scope = scope;
// map_set(&m->debug_info, hash_entity(e), di);
// return di;
}
return nullptr;
}
// TODO(lachsinc): Should we traverse the node/children until we find one with
@@ -2607,8 +2621,8 @@ irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *sco
return di;
}
void ir_push_debug_location(irModule *m, Ast *node, irDebugInfo *scope) {
irDebugInfo *debug_location = ir_add_debug_info_location(m, node, scope);
void ir_push_debug_location(irModule *m, Ast *node, irDebugInfo *scope, Entity *e) {
irDebugInfo *debug_location = ir_add_debug_info_location(m, node, scope, e);
array_add(&m->debug_location_stack, debug_location);
}
@@ -2666,9 +2680,18 @@ void ir_value_set_debug_location(irProcedure *proc, irValue *v) {
v->loc = *array_end_ptr(&m->debug_location_stack);
if (v->loc == nullptr && proc->entity != nullptr) {
// NOTE(lachsinc): Entry point (main()) and runtime_startup are the only ones where null location is considered valid.
GB_ASSERT_MSG(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0),
"%.*s %p", LIT(proc->name), proc->entity);
if (proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0)) {
// NOTE(lachsinc): Entry point (main()) and runtime_startup are the only ones where null location is considered valid.
} else {
if (v->kind == irValue_Instr) {
auto *instr = &v->Instr;
gb_printf_err("Instruction kind: %.*s\n", LIT(ir_instr_strings[instr->kind]));
if (instr->kind == irInstr_DebugDeclare) {
gb_printf_err("\t%.*s\n", LIT(instr->DebugDeclare.entity->token.string));
}
}
GB_PANIC("Value wihout debug location: %.*s %p; %p", LIT(proc->name), proc->entity, v);
}
}
}
@@ -3823,6 +3846,37 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
return ir_emit_runtime_call(proc, runtime_proc, args);
}
if (is_type_bit_set(a)) {
switch (op_kind) {
case Token_Lt:
case Token_LtEq:
case Token_Gt:
case Token_GtEq:
{
Type *it = bit_set_to_int(a);
irValue *lhs = ir_emit_bitcast(proc, left, it);
irValue *rhs = ir_emit_bitcast(proc, right, it);
irValue *res = ir_emit_arith(proc, Token_And, lhs, rhs, it);
if (op_kind == Token_Lt || op_kind == Token_LtEq) {
// (lhs & rhs) == lhs
res = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, res, lhs, t_llvm_bool));
} else if (op_kind == Token_Gt || op_kind == Token_GtEq) {
// (lhs & rhs) == rhs
res = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, res, rhs, t_llvm_bool));
}
// NOTE(bill): Strict subsets
if (op_kind == Token_Lt || op_kind == Token_Gt) {
// res &~ (lhs == rhs)
irValue *eq = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, lhs, rhs, t_llvm_bool));
res = ir_emit_arith(proc, Token_AndNot, res, eq, t_llvm_bool);
}
return res;
}
}
}
return ir_emit(proc, ir_instr_binary_op(proc, op_kind, left, right, t_llvm_bool));
}
@@ -4276,7 +4330,6 @@ void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *va
gbAllocator a = ir_allocator();
irValue *underlying = ir_emit_conv(proc, parent, alloc_type_pointer(variant_type));
irValue *v = variant;
ir_emit_store(proc, underlying, variant);
Type *t = type_deref(ir_type(parent));
@@ -4684,6 +4737,14 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
return ir_emit_ptr_to_uintptr(proc, value, t);
}
if (is_type_integer(src) && (is_type_pointer(dst) || is_type_cstring(dst))) {
Type *vt = core_type(ir_type(value));
return ir_emit(proc, ir_instr_conv(proc, irConv_inttoptr, value, vt, t));
}else if ((is_type_pointer(src) || is_type_cstring(src)) && is_type_integer(dst)) {
Type *vt = core_type(ir_type(value));
return ir_emit(proc, ir_instr_conv(proc, irConv_ptrtoint, value, vt, t));
}
if (ir_is_type_aggregate(src) || ir_is_type_aggregate(dst)) {
irValue *s = ir_address_from_load_or_generate_local(proc, value);
irValue *d = ir_emit_bitcast(proc, s, alloc_type_pointer(t));
@@ -6160,7 +6221,6 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
Type *key_type = rt->BitSet.elem;
GB_ASSERT(are_types_identical(ir_type(left), key_type));
Type *it = bit_set_to_int(rt);
left = ir_emit_conv(proc, left, it);
@@ -6965,9 +7025,18 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
GB_ASSERT(ir_type(field_expr)->kind != Type_Tuple);
irValue *fv = ir_emit_conv(proc, field_expr, ft);
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
ir_emit_store(proc, gep, fv);
Type *fet = ir_type(field_expr);
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
ir_emit_store_union_variant(proc, gep, field_expr, fet);
} else {
irValue *fv = ir_emit_conv(proc, field_expr, ft);
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
ir_emit_store(proc, gep, fv);
}
}
}
break;
@@ -8505,7 +8574,6 @@ void ir_begin_procedure_body(irProcedure *proc) {
ir_push_debug_location(proc->module, proc->entity->identifier, proc->debug_scope);
GB_ASSERT_NOT_NULL(proc->debug_scope);
} else {
// GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
ir_push_debug_location(proc->module, nullptr, nullptr);
}
+38 -6
View File
@@ -238,6 +238,32 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct = false
void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint);
void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
// NOTE(bill): This is written like this as it may need to
// changed for specific alignments
switch (alignment) {
case 1:
ir_write_string(f, str_lit("[0 x i8]"));
break;
case 2:
ir_write_string(f, str_lit("[0 x i16]"));
break;
case 4:
ir_write_string(f, str_lit("[0 x i32]"));
break;
case 8:
ir_write_string(f, str_lit("[0 x i64]"));
break;
case 16:
ir_write_string(f, str_lit("[0 x <4 x i32>]"));
break;
default:
GB_PANIC("Invalid alignment");
break;
}
}
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
GB_ASSERT(is_type_proc(t));
t = base_type(t);
@@ -398,8 +424,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
i64 align = type_align_of(t);
i64 block_size = t->Union.variant_block_size;
ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
ir_fprintf(f, "[%lld x i8], ", block_size);
ir_write_byte(f, '{');
ir_print_alignment_prefix_hack(f, align);
ir_fprintf(f, ", [%lld x i8], ", block_size);
// ir_print_type(f, m, t_type_info_ptr);
ir_print_type(f, m, union_tag_type(t));
ir_write_byte(f, '}');
@@ -412,7 +439,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 size_of_union = type_size_of(t);
i64 align_of_union = type_align_of(t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
ir_write_byte(f, '{');
ir_print_alignment_prefix_hack(f, align_of_union);
ir_fprintf(f, ", [%lld x i8]}", align_of_union, size_of_union);
return;
} else {
if (t->Struct.is_packed) {
@@ -420,7 +449,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
}
ir_write_byte(f, '{');
if (t->Struct.custom_align > 0) {
ir_fprintf(f, "[0 x <%lld x i8>]", t->Struct.custom_align);
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
if (t->Struct.fields.count > 0) {
ir_write_string(f, str_lit(", "));
}
@@ -499,7 +528,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
case Type_BitField: {
i64 align = type_align_of(t);
i64 size = type_size_of(t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align, size);
ir_write_byte(f, '{');
ir_print_alignment_prefix_hack(f, align);
ir_fprintf(f, ", [%lld x i8]}", size);
break;
}
@@ -1975,6 +2006,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
}
}
void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) {
GB_ASSERT(v->kind == irValue_TypeName);
Type *t = base_type(v->TypeName.type);
@@ -1998,7 +2030,7 @@ void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) {
}
ir_write_byte(f, '{');
if (t->Struct.custom_align > 0) {
ir_fprintf(f, "[0 x <%lld x i8>]", t->Struct.custom_align);
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
}
ir_write_byte(f, '}');
if (t->Struct.is_packed) {
+6
View File
@@ -214,6 +214,7 @@ enum BuildFlagKind {
BuildFlag_NoBoundsCheck,
BuildFlag_NoCRT,
BuildFlag_UseLLD,
BuildFlag_Vet,
BuildFlag_COUNT,
};
@@ -256,6 +257,7 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
GB_ASSERT(args.count >= 3);
Array<String> flag_args = array_slice(args, 3, args.count);
@@ -563,6 +565,10 @@ bool parse_build_flags(Array<String> args) {
case BuildFlag_UseLLD:
build_context.use_lld = true;
break;
case BuildFlag_Vet:
build_context.vet = true;
break;
}
}
+4 -1
View File
@@ -4567,7 +4567,10 @@ GB_THREAD_PROC(parse_worker_file_proc) {
if (thread == nullptr) return 0;
auto *p = cast(Parser *)thread->user_data;
isize index = thread->user_index;
ParseFileError err = process_imported_file(p, p->files_to_process[index]);
gb_mutex_lock(&p->file_add_mutex);
auto file_to_process = p->files_to_process[index];
gb_mutex_unlock(&p->file_add_mutex);
ParseFileError err = process_imported_file(p, file_to_process);
return cast(isize)err;
}
+39 -5
View File
@@ -215,6 +215,11 @@ String const type_strings[] = {
TYPE_KINDS
#undef TYPE_KIND
enum TypeFlag : u32 {
TypeFlag_Polymorphic = 1<<1,
TypeFlag_PolySpecialized = 1<<2,
};
struct Type {
TypeKind kind;
union {
@@ -226,6 +231,7 @@ struct Type {
// NOTE(bill): These need to be at the end to not affect the unionized data
i64 cached_size;
i64 cached_align;
u32 flags; // TypeFlag
bool failure;
};
@@ -1072,6 +1078,7 @@ bool is_type_indexable(Type *t) {
return false;
}
bool is_type_polymorphic_record(Type *t) {
t = base_type(t);
if (t->kind == Type_Struct) {
@@ -1082,6 +1089,18 @@ bool is_type_polymorphic_record(Type *t) {
return false;
}
Scope *polymorphic_record_parent_scope(Type *t) {
t = base_type(t);
if (is_type_polymorphic_record(t)) {
if (t->kind == Type_Struct) {
return t->Struct.scope->parent;
} else if (t->kind == Type_Union) {
return t->Union.scope->parent;
}
}
return nullptr;
}
bool is_type_polymorphic_record_specialized(Type *t) {
t = base_type(t);
if (t->kind == Type_Struct) {
@@ -1180,11 +1199,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
if (or_specialized && t->Union.is_poly_specialized) {
return true;
}
for_array(i, t->Union.variants) {
if (is_type_polymorphic(t->Union.variants[i], or_specialized)) {
return true;
}
}
// for_array(i, t->Union.variants) {
// if (is_type_polymorphic(t->Union.variants[i], or_specialized)) {
// return true;
// }
// }
break;
case Type_Struct:
if (t->Struct.is_polymorphic) {
@@ -1613,11 +1632,26 @@ i64 union_tag_size(Type *u) {
return 0;
}
#if 1
// TODO(bill): Is this an okay approach?
i64 max_align = 1;
for_array(i, u->Union.variants) {
Type *variant_type = u->Union.variants[i];
i64 align = type_align_of(variant_type);
if (max_align < align) {
max_align = align;
}
}
u->Union.tag_size = gb_min(max_align, build_context.max_align);
return max_align;
#else
i64 bytes = next_pow2(cast(i64)(floor_log2(n)/8 + 1));
i64 tag_size = gb_max(bytes, 1);
u->Union.tag_size = tag_size;
return tag_size;
#endif
}
Type *union_tag_type(Type *u) {