mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-13 06:41:26 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -888,6 +888,39 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
|
||||
return true;
|
||||
}
|
||||
|
||||
case BuiltinProc_simd_extract_lsbs:
|
||||
case BuiltinProc_simd_extract_msbs:
|
||||
{
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
|
||||
|
||||
if (!is_type_simd_vector(x.type)) {
|
||||
gbString xs = type_to_string(x.type);
|
||||
error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), xs);
|
||||
gb_string_free(xs);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *elem = base_array_type(x.type);
|
||||
if (!is_type_integer_like(elem)) {
|
||||
gbString xs = type_to_string(x.type);
|
||||
error(x.expr, "'%.*s' expected a #simd type with integer or boolean elements, got '%s'", LIT(builtin_name), xs);
|
||||
gb_string_free(xs);
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 num_elems = get_array_type_count(x.type);
|
||||
|
||||
Type *result_type = alloc_type_bit_set();
|
||||
result_type->BitSet.elem = t_int;
|
||||
result_type->BitSet.lower = 0;
|
||||
result_type->BitSet.upper = num_elems - 1;
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = result_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
case BuiltinProc_simd_shuffle:
|
||||
{
|
||||
|
||||
+8
-2
@@ -1742,8 +1742,8 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
|
||||
rw_mutex_shared_lock(&decl->type_info_deps_mutex);
|
||||
rw_mutex_lock(&decl->parent->type_info_deps_mutex);
|
||||
|
||||
for (Type *t : decl->type_info_deps) {
|
||||
ptr_set_add(&decl->parent->type_info_deps, t);
|
||||
for (auto const &tt : decl->type_info_deps) {
|
||||
type_set_add(&decl->parent->type_info_deps, tt);
|
||||
}
|
||||
|
||||
rw_mutex_unlock(&decl->parent->type_info_deps_mutex);
|
||||
@@ -1784,6 +1784,10 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
ctx->curr_proc_sig = type;
|
||||
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
|
||||
|
||||
if (decl->parent && decl->entity && decl->parent->entity) {
|
||||
decl->entity->parent_proc_decl = decl->parent;
|
||||
}
|
||||
|
||||
if (ctx->pkg->name != "runtime") {
|
||||
switch (type->Proc.calling_convention) {
|
||||
case ProcCC_None:
|
||||
@@ -1873,6 +1877,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
|
||||
check_open_scope(ctx, body);
|
||||
{
|
||||
ctx->scope->decl_info = decl;
|
||||
|
||||
for (auto const &entry : using_entities) {
|
||||
Entity *uvar = entry.uvar;
|
||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||
|
||||
+8
-4
@@ -345,7 +345,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
|
||||
check_collect_entities(c, nodes);
|
||||
|
||||
for (auto const &entry : s->elements) {
|
||||
Entity *e = entry.value;
|
||||
Entity *e = entry.value;\
|
||||
switch (e->kind) {
|
||||
case Entity_Constant:
|
||||
case Entity_TypeName:
|
||||
@@ -2599,9 +2599,8 @@ gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BigInt shift_amount = f->Constant.value.value_integer;
|
||||
big_int_sub_eq(&shift_amount, &b_lower_base);
|
||||
|
||||
BigInt shift_amount = {};
|
||||
big_int_sub(&shift_amount, &f->Constant.value.value_integer, &b_lower_base);
|
||||
|
||||
BigInt value = {};
|
||||
big_int_shl(&value, &one, &shift_amount);
|
||||
@@ -5509,6 +5508,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
case Addressing_SwizzleVariable:
|
||||
operand->mode = Addressing_SwizzleVariable;
|
||||
break;
|
||||
case Addressing_Value:
|
||||
if (is_type_pointer(original_type)) {
|
||||
operand->mode = Addressing_SwizzleVariable;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->kind == Type_SimdVector) {
|
||||
|
||||
+4
-4
@@ -1446,8 +1446,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
|
||||
|
||||
|
||||
Ast *nil_seen = nullptr;
|
||||
PtrSet<Type *> seen = {};
|
||||
defer (ptr_set_destroy(&seen));
|
||||
TypeSet seen = {};
|
||||
defer (type_set_destroy(&seen));
|
||||
|
||||
for (Ast *stmt : bs->stmts) {
|
||||
if (stmt->kind != Ast_CaseClause) {
|
||||
@@ -1515,7 +1515,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
|
||||
GB_PANIC("Unknown type to type switch statement");
|
||||
}
|
||||
|
||||
if (type_ptr_set_update(&seen, y.type)) {
|
||||
if (type_set_update(&seen, y.type)) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(y.expr,
|
||||
@@ -1569,7 +1569,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
|
||||
auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count);
|
||||
|
||||
for (Type *t : variants) {
|
||||
if (!type_ptr_set_exists(&seen, t)) {
|
||||
if (!type_set_exists(&seen, t)) {
|
||||
array_add(&unhandled, t);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -2859,15 +2859,23 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node)
|
||||
}
|
||||
|
||||
if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
|
||||
gbString s = expr_to_string(row.expr);
|
||||
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
if (row.expr == nullptr) {
|
||||
error(node, "Invalid matrix row count, got nothing");
|
||||
} else {
|
||||
gbString s = expr_to_string(row.expr);
|
||||
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
|
||||
gbString s = expr_to_string(column.expr);
|
||||
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
if (column.expr == nullptr) {
|
||||
error(node, "Invalid matrix column count, got nothing");
|
||||
} else {
|
||||
gbString s = expr_to_string(column.expr);
|
||||
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
|
||||
|
||||
+110
-51
@@ -3,7 +3,10 @@
|
||||
#include "entity.cpp"
|
||||
#include "types.cpp"
|
||||
|
||||
String get_final_microarchitecture();
|
||||
|
||||
gb_internal u64 type_hash_canonical_type(Type *type);
|
||||
|
||||
gb_internal String get_final_microarchitecture();
|
||||
|
||||
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
|
||||
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
|
||||
@@ -170,7 +173,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
d->parent = parent;
|
||||
d->scope = scope;
|
||||
ptr_set_init(&d->deps, 0);
|
||||
ptr_set_init(&d->type_info_deps, 0);
|
||||
type_set_init(&d->type_info_deps, 0);
|
||||
d->labels.allocator = heap_allocator();
|
||||
d->variadic_reuses.allocator = heap_allocator();
|
||||
d->variadic_reuse_max_bytes = 0;
|
||||
@@ -355,6 +358,10 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
|
||||
scope->flags |= ScopeFlag_Type;
|
||||
break;
|
||||
}
|
||||
if (c->decl && c->decl->proc_lit) {
|
||||
// Number the scopes within a procedure body depth-first
|
||||
scope->index = c->decl->scope_index++;
|
||||
}
|
||||
c->scope = scope;
|
||||
c->state_flags |= StateFlag_bounds_check;
|
||||
}
|
||||
@@ -825,11 +832,17 @@ gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
|
||||
rw_mutex_unlock(&d->deps_mutex);
|
||||
}
|
||||
gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) {
|
||||
if (d == nullptr) {
|
||||
if (d == nullptr || type == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (type->kind == Type_Named) {
|
||||
Entity *e = type->Named.type_name;
|
||||
if (e->TypeName.is_type_alias) {
|
||||
type = type->Named.base;
|
||||
}
|
||||
}
|
||||
rw_mutex_lock(&d->type_info_deps_mutex);
|
||||
ptr_set_add(&d->type_info_deps, type);
|
||||
type_set_add(&d->type_info_deps, type);
|
||||
rw_mutex_unlock(&d->type_info_deps_mutex);
|
||||
}
|
||||
|
||||
@@ -1358,8 +1371,11 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
||||
string_map_init(&i->foreigns);
|
||||
// map_init(&i->gen_procs);
|
||||
map_init(&i->gen_types);
|
||||
array_init(&i->type_info_types, a);
|
||||
map_init(&i->type_info_map);
|
||||
|
||||
type_set_init(&i->min_dep_type_info_set);
|
||||
map_init(&i->min_dep_type_info_index_map);
|
||||
|
||||
// map_init(&i->type_info_map);
|
||||
string_map_init(&i->files);
|
||||
string_map_init(&i->packages);
|
||||
array_init(&i->variable_init_order, a);
|
||||
@@ -1392,8 +1408,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
|
||||
string_map_destroy(&i->foreigns);
|
||||
// map_destroy(&i->gen_procs);
|
||||
map_destroy(&i->gen_types);
|
||||
array_free(&i->type_info_types);
|
||||
map_destroy(&i->type_info_map);
|
||||
|
||||
type_set_destroy(&i->min_dep_type_info_set);
|
||||
map_destroy(&i->min_dep_type_info_index_map);
|
||||
|
||||
string_map_destroy(&i->files);
|
||||
string_map_destroy(&i->packages);
|
||||
array_free(&i->variable_init_order);
|
||||
@@ -1627,6 +1645,23 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) {
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) {
|
||||
mutex_lock(&info->minimum_dependency_type_info_mutex);
|
||||
|
||||
isize entry_index = -1;
|
||||
u64 hash = pair.hash;
|
||||
isize *found_entry_index = map_get(&info->min_dep_type_info_index_map, hash);
|
||||
if (found_entry_index) {
|
||||
entry_index = *found_entry_index;
|
||||
}
|
||||
mutex_unlock(&info->minimum_dependency_type_info_mutex);
|
||||
|
||||
if (error_on_failure && entry_index < 0) {
|
||||
compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type));
|
||||
}
|
||||
return entry_index;
|
||||
}
|
||||
|
||||
|
||||
gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
|
||||
type = default_type(type);
|
||||
@@ -1634,34 +1669,12 @@ gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_f
|
||||
type = t_bool;
|
||||
}
|
||||
|
||||
mutex_lock(&info->type_info_mutex);
|
||||
|
||||
isize entry_index = -1;
|
||||
isize *found_entry_index = map_get(&info->type_info_map, type);
|
||||
if (found_entry_index) {
|
||||
entry_index = *found_entry_index;
|
||||
}
|
||||
if (entry_index < 0) {
|
||||
// NOTE(bill): Do manual linear search
|
||||
for (auto const &e : info->type_info_map) {
|
||||
if (are_types_identical_unique_tuples(e.key, type)) {
|
||||
entry_index = e.value;
|
||||
// NOTE(bill): Add it to the search map
|
||||
map_set(&info->type_info_map, type, entry_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&info->type_info_mutex);
|
||||
|
||||
if (error_on_failure && entry_index < 0) {
|
||||
compiler_error("Type_Info for '%s' could not be found", type_to_string(type));
|
||||
}
|
||||
return entry_index;
|
||||
u64 hash = type_hash_canonical_type(type);
|
||||
return type_info_index(info, {type, hash}, error_on_failure);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
|
||||
if (expr == nullptr) {
|
||||
return;
|
||||
@@ -2013,8 +2026,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
}
|
||||
|
||||
add_type_info_dependency(c->info, c->decl, t);
|
||||
|
||||
#if 0
|
||||
MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) {
|
||||
if (type_set_update(&c->info->type_info_set, t)) {
|
||||
// return;
|
||||
}
|
||||
|
||||
auto found = map_get(&c->info->type_info_map, t);
|
||||
if (found != nullptr) {
|
||||
// Types have already been added
|
||||
@@ -2037,7 +2054,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
// Unique entry
|
||||
// NOTE(bill): map entries grow linearly and in order
|
||||
ti_index = c->info->type_info_types.count;
|
||||
array_add(&c->info->type_info_types, t);
|
||||
TypeInfoPair tt = {t, type_hash_canonical_type(t)};
|
||||
array_add(&c->info->type_info_types, tt);
|
||||
}
|
||||
map_set(&c->checker->info.type_info_map, t, ti_index);
|
||||
|
||||
@@ -2232,6 +2250,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -2289,19 +2308,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *set = &c->info.minimum_dependency_type_info_set;
|
||||
|
||||
isize ti_index = type_info_index(&c->info, t, false);
|
||||
if (ti_index < 0) {
|
||||
add_type_info_type(&c->builtin_ctx, t); // Missing the type information
|
||||
ti_index = type_info_index(&c->info, t, false);
|
||||
}
|
||||
GB_ASSERT(ti_index >= 0);
|
||||
// IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref
|
||||
// and effectively assigns the `+1` of the value
|
||||
isize const count = set->count;
|
||||
if (map_set_if_not_previously_exists(set, ti_index+1, count)) {
|
||||
// Type already exists;
|
||||
if (type_set_update(&c->info.min_dep_type_info_set, t)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2501,8 +2508,8 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
|
||||
if (decl == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (Type *t : decl->type_info_deps) {
|
||||
add_min_dep_type_info(c, t);
|
||||
for (TypeInfoPair const tt : decl->type_info_deps) {
|
||||
add_min_dep_type_info(c, tt.type);
|
||||
}
|
||||
|
||||
for (Entity *e : decl->deps) {
|
||||
@@ -2702,7 +2709,6 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||
isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
|
||||
|
||||
ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
|
||||
map_init(&c->info.minimum_dependency_type_info_set);
|
||||
|
||||
#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
|
||||
if (condition) { \
|
||||
@@ -3894,6 +3900,7 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
||||
#include "check_expr.cpp"
|
||||
#include "check_builtin.cpp"
|
||||
#include "check_type.cpp"
|
||||
#include "name_canonicalization.cpp"
|
||||
#include "check_decl.cpp"
|
||||
#include "check_stmt.cpp"
|
||||
|
||||
@@ -6724,6 +6731,58 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
add_type_and_value(&c->builtin_ctx, u.expr, u.info->mode, u.info->type, u.info->value);
|
||||
}
|
||||
|
||||
TIME_SECTION("initialize and check for collisions in type info array");
|
||||
{
|
||||
Array<TypeInfoPair> type_info_types; // sorted after filled
|
||||
array_init(&type_info_types, heap_allocator());
|
||||
defer (array_free(&type_info_types));
|
||||
|
||||
for (auto const &tt : c->info.min_dep_type_info_set) {
|
||||
array_add(&type_info_types, tt);
|
||||
}
|
||||
array_sort(type_info_types, type_info_pair_cmp);
|
||||
|
||||
array_init(&c->info.type_info_types_hash_map, heap_allocator(), type_info_types.count*2 + 1);
|
||||
map_reserve(&c->info.min_dep_type_info_index_map, type_info_types.count);
|
||||
|
||||
isize hash_map_len = c->info.type_info_types_hash_map.count;
|
||||
for (auto const &tt : type_info_types) {
|
||||
isize index = tt.hash % hash_map_len;
|
||||
// NOTE(bill): no need for a sanity check since there
|
||||
// will always be enough space for the entries
|
||||
for (;;) {
|
||||
if (index == 0 || c->info.type_info_types_hash_map[index].hash != 0) {
|
||||
index = (index+1) % hash_map_len;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
c->info.type_info_types_hash_map[index] = tt;
|
||||
|
||||
bool exists = map_set_if_not_previously_exists(&c->info.min_dep_type_info_index_map, tt.hash, index);
|
||||
if (exists) {
|
||||
for (auto const &entry : c->info.min_dep_type_info_index_map) {
|
||||
if (entry.key != tt.hash) {
|
||||
continue;
|
||||
}
|
||||
auto const &other = type_info_types[entry.value];
|
||||
if (are_types_identical_unique_tuples(tt.type, other.type)) {
|
||||
continue;
|
||||
}
|
||||
gbString t = temp_canonical_string(tt.type);
|
||||
gbString o = temp_canonical_string(other.type);
|
||||
GB_PANIC("%s (%s) %llu vs %s (%s) %llu",
|
||||
type_to_string(tt.type, false), t, cast(unsigned long long)tt.hash,
|
||||
type_to_string(other.type, false), o, cast(unsigned long long)other.hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GB_ASSERT(c->info.min_dep_type_info_index_map.count <= type_info_types.count);
|
||||
}
|
||||
|
||||
|
||||
TIME_SECTION("sort init and fini procedures");
|
||||
check_sort_init_and_fini_procedures(c);
|
||||
|
||||
|
||||
+18
-7
@@ -167,6 +167,7 @@ typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
|
||||
|
||||
gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
|
||||
#include "name_canonicalization.hpp"
|
||||
|
||||
enum ProcCheckedState : u8 {
|
||||
ProcCheckedState_Unchecked,
|
||||
@@ -221,13 +222,15 @@ struct DeclInfo {
|
||||
RwMutex deps_mutex;
|
||||
PtrSet<Entity *> deps;
|
||||
|
||||
RwMutex type_info_deps_mutex;
|
||||
PtrSet<Type *> type_info_deps;
|
||||
RwMutex type_info_deps_mutex;
|
||||
TypeSet type_info_deps;
|
||||
|
||||
BlockingMutex type_and_value_mutex;
|
||||
|
||||
Array<BlockLabel> labels;
|
||||
|
||||
i32 scope_index;
|
||||
|
||||
Array<VariadicReuseData> variadic_reuses;
|
||||
i64 variadic_reuse_max_bytes;
|
||||
i64 variadic_reuse_max_align;
|
||||
@@ -272,10 +275,14 @@ struct Scope {
|
||||
std::atomic<Scope *> next;
|
||||
std::atomic<Scope *> head_child;
|
||||
|
||||
i32 index; // within a procedure
|
||||
|
||||
RwMutex mutex;
|
||||
StringMap<Entity *> elements;
|
||||
PtrSet<Scope *> imported;
|
||||
|
||||
DeclInfo *decl_info;
|
||||
|
||||
i32 flags; // ScopeFlag
|
||||
union {
|
||||
AstPackage *pkg;
|
||||
@@ -421,8 +428,10 @@ struct CheckerInfo {
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
PtrMap</*type info index*/isize, /*min dep index*/isize> minimum_dependency_type_info_set;
|
||||
|
||||
BlockingMutex minimum_dependency_type_info_mutex;
|
||||
PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
|
||||
TypeSet min_dep_type_info_set;
|
||||
Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count
|
||||
|
||||
|
||||
Array<Entity *> testing_procedures;
|
||||
@@ -450,9 +459,10 @@ struct CheckerInfo {
|
||||
BlockingMutex gen_types_mutex;
|
||||
PtrMap<Type *, GenTypesData *> gen_types;
|
||||
|
||||
BlockingMutex type_info_mutex; // NOT recursive
|
||||
Array<Type *> type_info_types;
|
||||
PtrMap<Type *, isize> type_info_map;
|
||||
// BlockingMutex type_info_mutex; // NOT recursive
|
||||
// Array<TypeInfoPair> type_info_types;
|
||||
// PtrMap<Type *, isize> type_info_map;
|
||||
// TypeSet type_info_set;
|
||||
|
||||
BlockingMutex foreign_mutex; // NOT recursive
|
||||
StringMap<Entity *> foreigns;
|
||||
@@ -571,6 +581,7 @@ gb_internal DeclInfo * decl_info_of_entity (Entity * e);
|
||||
gb_internal AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
|
||||
// IMPORTANT: Only to use once checking is done
|
||||
gb_internal isize type_info_index (CheckerInfo *i, Type *type, bool error_on_failure);
|
||||
gb_internal isize type_info_index (CheckerInfo *info, TypeInfoPair pair, bool error_on_failure);
|
||||
|
||||
// Will return nullptr if not found
|
||||
gb_internal Entity *entity_of_node(Ast *expr);
|
||||
|
||||
@@ -181,6 +181,9 @@ BuiltinProc__simd_begin,
|
||||
BuiltinProc_simd_reduce_any,
|
||||
BuiltinProc_simd_reduce_all,
|
||||
|
||||
BuiltinProc_simd_extract_lsbs,
|
||||
BuiltinProc_simd_extract_msbs,
|
||||
|
||||
BuiltinProc_simd_shuffle,
|
||||
BuiltinProc_simd_select,
|
||||
|
||||
@@ -523,6 +526,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("simd_reduce_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("simd_reduce_all"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("simd_extract_lsbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("simd_extract_msbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
+2
-2
@@ -134,9 +134,9 @@ gb_internal u32 fnv32a(void const *data, isize len) {
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_internal u64 fnv64a(void const *data, isize len) {
|
||||
gb_internal u64 fnv64a(void const *data, isize len, u64 seed=0xcbf29ce484222325ull) {
|
||||
u8 const *bytes = cast(u8 const *)data;
|
||||
u64 h = 0xcbf29ce484222325ull;
|
||||
u64 h = seed;
|
||||
|
||||
for (; len >= 8; len -= 8, bytes += 8) {
|
||||
h = (h ^ bytes[0]) * 0x100000001b3ull;
|
||||
|
||||
+18
-47
@@ -16,6 +16,8 @@ gb_global char const* OdinDocWriterState_strings[] {
|
||||
"writing ",
|
||||
};
|
||||
|
||||
gb_global std::atomic<bool> g_in_doc_writer;
|
||||
|
||||
struct OdinDocWriter {
|
||||
CheckerInfo *info;
|
||||
OdinDocWriterState state;
|
||||
@@ -26,11 +28,10 @@ struct OdinDocWriter {
|
||||
|
||||
StringMap<OdinDocString> string_cache;
|
||||
|
||||
OrderedInsertPtrMap<AstFile *, OdinDocFileIndex> file_cache;
|
||||
OrderedInsertPtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
|
||||
OrderedInsertPtrMap<Entity *, OdinDocEntityIndex> entity_cache;
|
||||
OrderedInsertPtrMap<Type *, OdinDocTypeIndex> type_cache;
|
||||
OrderedInsertPtrMap<Type *, Type *> stable_type_cache;
|
||||
OrderedInsertPtrMap<AstFile *, OdinDocFileIndex> file_cache;
|
||||
OrderedInsertPtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
|
||||
OrderedInsertPtrMap<Entity *, OdinDocEntityIndex> entity_cache;
|
||||
OrderedInsertPtrMap<u64/*type hash*/, OdinDocTypeIndex> type_cache;
|
||||
|
||||
OdinDocWriterItemTracker<OdinDocFile> files;
|
||||
OdinDocWriterItemTracker<OdinDocPkg> pkgs;
|
||||
@@ -61,7 +62,6 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
|
||||
map_init(&w->pkg_cache, 1<<10);
|
||||
map_init(&w->entity_cache, 1<<18);
|
||||
map_init(&w->type_cache, 1<<18);
|
||||
map_init(&w->stable_type_cache, 1<<18);
|
||||
|
||||
odin_doc_writer_item_tracker_init(&w->files, 1);
|
||||
odin_doc_writer_item_tracker_init(&w->pkgs, 1);
|
||||
@@ -81,7 +81,6 @@ gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
|
||||
map_destroy(&w->pkg_cache);
|
||||
map_destroy(&w->entity_cache);
|
||||
map_destroy(&w->type_cache);
|
||||
map_destroy(&w->stable_type_cache);
|
||||
}
|
||||
|
||||
|
||||
@@ -492,55 +491,18 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
|
||||
}
|
||||
}
|
||||
|
||||
// Type **mapped_type = map_get(&w->stable_type_cache, type); // may map to itself
|
||||
// if (mapped_type && *mapped_type) {
|
||||
// type = *mapped_type;
|
||||
// }
|
||||
|
||||
OdinDocTypeIndex *found = map_get(&w->type_cache, type);
|
||||
u64 type_hash = type_hash_canonical_type(type);
|
||||
OdinDocTypeIndex *found = map_get(&w->type_cache, type_hash);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
for (auto const &entry : w->type_cache) {
|
||||
// NOTE(bill): THIS IS SLOW
|
||||
Type *x = type;
|
||||
Type *y = entry.key;
|
||||
|
||||
if (x == y) {
|
||||
goto do_set;
|
||||
}
|
||||
|
||||
if (!x | !y) {
|
||||
continue;
|
||||
}
|
||||
if (y->kind == Type_Named) {
|
||||
Entity *e = y->Named.type_name;
|
||||
if (e->TypeName.is_type_alias) {
|
||||
y = y->Named.base;
|
||||
}
|
||||
}
|
||||
if (x->kind != y->kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!are_types_identical_internal(x, y, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do_set:
|
||||
OdinDocTypeIndex index = entry.value;
|
||||
map_set(&w->type_cache, type, index);
|
||||
map_set(&w->stable_type_cache, type, entry.key);
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
OdinDocType *dst = nullptr;
|
||||
OdinDocType doc_type = {};
|
||||
OdinDocTypeIndex type_index = 0;
|
||||
type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
|
||||
map_set(&w->type_cache, type, type_index);
|
||||
map_set(&w->stable_type_cache, type, type);
|
||||
map_set(&w->type_cache, type_hash, type_index);
|
||||
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
@@ -1177,6 +1139,8 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
|
||||
}
|
||||
|
||||
gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
|
||||
g_in_doc_writer.store(true);
|
||||
|
||||
OdinDocWriter w_ = {};
|
||||
OdinDocWriter *w = &w_;
|
||||
defer (odin_doc_writer_destroy(w));
|
||||
@@ -1192,4 +1156,11 @@ gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
|
||||
odin_doc_writer_end_writing(w);
|
||||
|
||||
odin_doc_write_to_file(w, filename);
|
||||
|
||||
g_in_doc_writer.store(false);
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_in_doc_writer(void) {
|
||||
return g_in_doc_writer.load();
|
||||
}
|
||||
@@ -257,6 +257,7 @@ struct Entity {
|
||||
bool has_instrumentation : 1;
|
||||
bool is_memcpy_like : 1;
|
||||
bool uses_branch_location : 1;
|
||||
bool is_anonymous : 1;
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
|
||||
+1
-1
@@ -5856,7 +5856,7 @@ gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
|
||||
|
||||
|
||||
gb_inline char *gb_bprintf_va(char const *fmt, va_list va) {
|
||||
gb_local_persist char buffer[4096];
|
||||
gb_thread_local gb_local_persist char buffer[4096];
|
||||
gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
+72
-78
@@ -24,7 +24,7 @@
|
||||
#include "llvm_backend_stmt.cpp"
|
||||
#include "llvm_backend_proc.cpp"
|
||||
|
||||
String get_default_microarchitecture() {
|
||||
gb_internal String get_default_microarchitecture() {
|
||||
String default_march = str_lit("generic");
|
||||
if (build_context.metrics.arch == TargetArch_amd64) {
|
||||
// NOTE(bill): x86-64-v2 is more than enough for everyone
|
||||
@@ -47,7 +47,7 @@ String get_default_microarchitecture() {
|
||||
return default_march;
|
||||
}
|
||||
|
||||
String get_final_microarchitecture() {
|
||||
gb_internal String get_final_microarchitecture() {
|
||||
BuildContext *bc = &build_context;
|
||||
|
||||
String microarch = bc->microarch;
|
||||
@@ -169,7 +169,7 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
|
||||
other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname);
|
||||
if (other_global) {
|
||||
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
|
||||
if (!ec.e->Variable.is_export) {
|
||||
if (!ec.e->Variable.is_export && !ec.e->Variable.is_foreign) {
|
||||
LLVMSetVisibility(other_global, LLVMHiddenVisibility);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
|
||||
other_global = LLVMGetNamedFunction(ec.other_module->mod, ec.cname);
|
||||
if (other_global) {
|
||||
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
|
||||
if (!ec.e->Procedure.is_export) {
|
||||
if (!ec.e->Procedure.is_export && !ec.e->Procedure.is_foreign) {
|
||||
LLVMSetVisibility(other_global, LLVMHiddenVisibility);
|
||||
}
|
||||
}
|
||||
@@ -233,6 +233,16 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
|
||||
}
|
||||
|
||||
|
||||
gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type) {
|
||||
gbString str = gb_string_make(permanent_allocator(), prefix);
|
||||
u64 hash = type_hash_canonical_type(type);
|
||||
str = gb_string_appendc(str, "-");
|
||||
str = gb_string_append_fmt(str, "%llu", cast(unsigned long long)hash);
|
||||
String proc_name = make_string(cast(u8 const *)str, gb_string_length(str));
|
||||
return proc_name;
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(is_type_comparable(type));
|
||||
@@ -240,7 +250,8 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
Type *pt = alloc_type_pointer(type);
|
||||
LLVMTypeRef ptr_type = lb_type(m, pt);
|
||||
|
||||
lbProcedure **found = map_get(&m->equal_procs, type);
|
||||
String proc_name = lb_internal_gen_name_from_type("__$equal", type);
|
||||
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
|
||||
lbProcedure *compare_proc = nullptr;
|
||||
if (found) {
|
||||
compare_proc = *found;
|
||||
@@ -248,17 +259,12 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
return {compare_proc->value, compare_proc->type};
|
||||
}
|
||||
|
||||
static std::atomic<u32> proc_index;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
|
||||
map_set(&m->equal_procs, type, p);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
// lb_add_attribute_to_proc(m, p->value, "readonly");
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
|
||||
@@ -410,24 +416,19 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
Type *pt = alloc_type_pointer(type);
|
||||
|
||||
lbProcedure **found = map_get(&m->hasher_procs, type);
|
||||
String proc_name = lb_internal_gen_name_from_type("__$hasher", type);
|
||||
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return {(*found)->value, (*found)->type};
|
||||
}
|
||||
|
||||
static std::atomic<u32> proc_index;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$hasher%u", 1+proc_index.fetch_add(1));
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc);
|
||||
map_set(&m->hasher_procs, type, p);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
// lb_add_attribute_to_proc(m, p->value, "readonly");
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
|
||||
@@ -577,21 +578,15 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
|
||||
|
||||
lbProcedure **found = map_get(&m->map_get_procs, type);
|
||||
String proc_name = lb_internal_gen_name_from_type("__$map_get", type);
|
||||
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return {(*found)->value, (*found)->type};
|
||||
}
|
||||
static std::atomic<u32> proc_index;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$map_get-%u", 1+proc_index.fetch_add(1));
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
|
||||
map_set(&m->map_get_procs, type, p);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
@@ -758,21 +753,15 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
|
||||
|
||||
lbProcedure **found = map_get(&m->map_set_procs, type);
|
||||
String proc_name = lb_internal_gen_name_from_type("__$map_set", type);
|
||||
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return {(*found)->value, (*found)->type};
|
||||
}
|
||||
static std::atomic<u32> proc_index;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1));
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
|
||||
map_set(&m->map_set_procs, type, p);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
@@ -917,7 +906,7 @@ gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
|
||||
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
|
||||
lbValue res = {llvm_res, t_map_cell_info};
|
||||
|
||||
lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_with_name(m, t_map_cell_info, res, lb_internal_gen_name_from_type("ggv$map_cell_info", type));
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
map_set(&m->map_cell_info_map, type, addr);
|
||||
@@ -948,7 +937,7 @@ gb_internal lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
|
||||
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
|
||||
lbValue res = {llvm_res, t_map_info};
|
||||
|
||||
lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_with_name(m, t_map_info, res, lb_internal_gen_name_from_type("ggv$map_info", map_type));
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
map_set(&m->map_info_map, map_type, addr);
|
||||
@@ -1284,7 +1273,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for 'any' type
|
||||
Type *var_type = default_type(var.init.type);
|
||||
lbAddr g = lb_add_global_generated(main_module, var_type, var.init);
|
||||
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
|
||||
gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
|
||||
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
|
||||
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
|
||||
lb_addr_store(p, g, var.init);
|
||||
lbValue gp = lb_addr_get_ptr(p, g);
|
||||
|
||||
@@ -1563,21 +1555,13 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &entry : m->equal_procs) {
|
||||
for (auto const &entry : m->gen_procs) {
|
||||
lbProcedure *p = entry.value;
|
||||
lb_llvm_function_pass_per_function_internal(m, p);
|
||||
}
|
||||
for (auto const &entry : m->hasher_procs) {
|
||||
lbProcedure *p = entry.value;
|
||||
lb_llvm_function_pass_per_function_internal(m, p);
|
||||
}
|
||||
for (auto const &entry : m->map_get_procs) {
|
||||
lbProcedure *p = entry.value;
|
||||
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
|
||||
}
|
||||
for (auto const &entry : m->map_set_procs) {
|
||||
lbProcedure *p = entry.value;
|
||||
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
|
||||
if (string_starts_with(p->name, str_lit("__$map"))) {
|
||||
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
|
||||
} else {
|
||||
lb_llvm_function_pass_per_function_internal(m, p);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2575,17 +2559,16 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) {
|
||||
build_context.build_paths[BuildPath_Output].name
|
||||
);
|
||||
|
||||
if (m->file) {
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
|
||||
String suffix = make_string((u8 *)buf, n-1);
|
||||
path = concatenate_strings(permanent_allocator(), path, suffix);
|
||||
} else if (m->pkg) {
|
||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||
} else if (USE_SEPARATE_MODULES) {
|
||||
path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin"));
|
||||
GB_ASSERT(m->module_name != nullptr);
|
||||
String s = make_string_c(m->module_name);
|
||||
String prefix = str_lit("odin_package-");
|
||||
if (string_starts_with(s, prefix)) {
|
||||
s.text += prefix.len;
|
||||
s.len -= prefix.len;
|
||||
}
|
||||
path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll"));
|
||||
|
||||
path = concatenate_strings(permanent_allocator(), path, s);
|
||||
path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll"));
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -2608,17 +2591,21 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
|
||||
path = gb_string_appendc(path, "/");
|
||||
path = gb_string_append_length(path, name.text, name.len);
|
||||
|
||||
if (m->file) {
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
|
||||
String suffix = make_string((u8 *)buf, n-1);
|
||||
path = gb_string_append_length(path, suffix.text, suffix.len);
|
||||
} else if (m->pkg) {
|
||||
path = gb_string_appendc(path, "-");
|
||||
path = gb_string_append_length(path, m->pkg->name.text, m->pkg->name.len);
|
||||
{
|
||||
|
||||
GB_ASSERT(m->module_name != nullptr);
|
||||
String s = make_string_c(m->module_name);
|
||||
String prefix = str_lit("odin_package");
|
||||
if (string_starts_with(s, prefix)) {
|
||||
s.text += prefix.len;
|
||||
s.len -= prefix.len;
|
||||
}
|
||||
|
||||
path = gb_string_append_length(path, s.text, s.len);
|
||||
}
|
||||
|
||||
if (use_temporary_directory) {
|
||||
// NOTE(bill): this must be suffixed to ensure it is not conflicting with anything else in the temporary directory
|
||||
path = gb_string_append_fmt(path, "-%p", m);
|
||||
}
|
||||
|
||||
@@ -2825,7 +2812,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
|
||||
Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count);
|
||||
Type *slice_type = alloc_type_slice(t_Internal_Test);
|
||||
lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {});
|
||||
lbAddr all_tests_array_addr = lb_add_global_generated_with_name(p->module, array_type, {}, str_lit("__$all_tests_array"));
|
||||
lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr);
|
||||
|
||||
LLVMValueRef indices[2] = {};
|
||||
@@ -3154,7 +3141,10 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
lbModule *m = default_module;
|
||||
|
||||
{ // Add type info data
|
||||
isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
|
||||
// GB_ASSERT_MSG(info->minimum_dependency_type_info_index_map.count == info->type_info_types.count, "%tu vs %tu", info->minimum_dependency_type_info_index_map.count, info->type_info_types.count);
|
||||
|
||||
// isize max_type_info_count = info->minimum_dependency_type_info_index_map.count+1;
|
||||
isize max_type_info_count = info->type_info_types_hash_map.count;
|
||||
Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count);
|
||||
|
||||
// IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized
|
||||
@@ -3166,7 +3156,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME);
|
||||
LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type));
|
||||
LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
|
||||
LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
|
||||
// LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetGlobalConstant(g, true);
|
||||
|
||||
lbValue value = {};
|
||||
@@ -3182,7 +3172,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
isize count = 0;
|
||||
isize offsets_extra = 0;
|
||||
|
||||
for (Type *t : m->info->type_info_types) {
|
||||
for (auto const &tt : m->info->type_info_types_hash_map) {
|
||||
Type *t = tt.type;
|
||||
if (t == nullptr) {
|
||||
continue;
|
||||
}
|
||||
isize index = lb_type_info_index(m->info, t, false);
|
||||
if (index < 0) {
|
||||
continue;
|
||||
|
||||
+18
-18
@@ -160,15 +160,17 @@ struct lbModule {
|
||||
AstFile *file; // possibly associated
|
||||
char const *module_name;
|
||||
|
||||
PtrMap<Type *, LLVMTypeRef> types; // mutex: types_mutex
|
||||
PtrMap<u64/*type hash*/, LLVMTypeRef> types; // mutex: types_mutex
|
||||
PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *, mutex: types_mutex
|
||||
PtrMap<Type *, LLVMTypeRef> func_raw_types; // mutex: func_raw_types_mutex
|
||||
RecursiveMutex types_mutex;
|
||||
RecursiveMutex func_raw_types_mutex;
|
||||
PtrMap<u64/*type hash*/, LLVMTypeRef> func_raw_types; // mutex: func_raw_types_mutex
|
||||
RecursiveMutex types_mutex;
|
||||
RecursiveMutex func_raw_types_mutex;
|
||||
i32 internal_type_level;
|
||||
|
||||
RwMutex values_mutex;
|
||||
|
||||
std::atomic<u32> global_array_index;
|
||||
|
||||
PtrMap<Entity *, lbValue> values;
|
||||
PtrMap<Entity *, lbAddr> soa_values;
|
||||
StringMap<lbValue> members;
|
||||
@@ -178,14 +180,9 @@ struct lbModule {
|
||||
|
||||
StringMap<LLVMValueRef> const_strings;
|
||||
|
||||
PtrMap<Type *, struct lbFunctionType *> function_type_map;
|
||||
PtrMap<u64/*type hash*/, struct lbFunctionType *> function_type_map;
|
||||
|
||||
PtrMap<Type *, lbProcedure *> equal_procs;
|
||||
PtrMap<Type *, lbProcedure *> hasher_procs;
|
||||
PtrMap<Type *, lbProcedure *> map_get_procs;
|
||||
PtrMap<Type *, lbProcedure *> map_set_procs;
|
||||
|
||||
std::atomic<u32> nested_type_name_guid;
|
||||
StringMap<lbProcedure *> gen_procs; // key is the canonicalized name
|
||||
|
||||
Array<lbProcedure *> procedures_to_generate;
|
||||
Array<Entity *> global_procedures_to_create;
|
||||
@@ -204,8 +201,8 @@ struct lbModule {
|
||||
StringMap<lbObjcRef> objc_classes;
|
||||
StringMap<lbObjcRef> objc_selectors;
|
||||
|
||||
PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
|
||||
PtrMap<Ast *, lbAddr> exact_value_compound_literal_addr_map; // Key: Ast_CompoundLit
|
||||
|
||||
@@ -231,9 +228,6 @@ struct lbGenerator : LinkerData {
|
||||
RecursiveMutex anonymous_proc_lits_mutex;
|
||||
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
|
||||
|
||||
std::atomic<u32> global_array_index;
|
||||
std::atomic<u32> global_generated_index;
|
||||
|
||||
isize used_module_count;
|
||||
|
||||
lbProcedure *startup_runtime;
|
||||
@@ -364,6 +358,8 @@ struct lbProcedure {
|
||||
bool in_multi_assignment;
|
||||
Array<LLVMValueRef> raw_input_parameters;
|
||||
|
||||
u32 global_generated_index;
|
||||
|
||||
bool uses_branch_location;
|
||||
TokenPos branch_location_pos;
|
||||
TokenPos curr_token_pos;
|
||||
@@ -399,7 +395,7 @@ struct lbProcedure {
|
||||
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
|
||||
|
||||
gb_internal String lb_mangle_name(Entity *e);
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e);
|
||||
|
||||
gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
|
||||
gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
|
||||
@@ -473,7 +469,8 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
|
||||
gb_internal lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
|
||||
|
||||
|
||||
gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
|
||||
gb_internal lbAddr lb_add_global_generated_from_procedure(lbProcedure *p, Type *type, lbValue value={});
|
||||
gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lbValue value, String name, Entity **entity_=nullptr);
|
||||
gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, bool force_no_init=false);
|
||||
|
||||
gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e);
|
||||
@@ -610,6 +607,9 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
|
||||
}
|
||||
|
||||
|
||||
gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type);
|
||||
|
||||
|
||||
gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value);
|
||||
gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name);
|
||||
|
||||
|
||||
+37
-11
@@ -330,31 +330,57 @@ gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node
|
||||
return lb_emit_source_code_location_const(p, proc_name, pos);
|
||||
}
|
||||
|
||||
gb_internal String lb_source_code_location_gen_name(String const &procedure, TokenPos const &pos) {
|
||||
gbString s = gb_string_make(permanent_allocator(), "scl$[");
|
||||
|
||||
s = gb_string_append_length(s, procedure.text, procedure.len);
|
||||
if (pos.offset != 0) {
|
||||
s = gb_string_append_fmt(s, "%d", pos.offset);
|
||||
} else {
|
||||
s = gb_string_append_fmt(s, "%d_%d", pos.line, pos.column);
|
||||
}
|
||||
s = gb_string_appendc(s, "]");
|
||||
|
||||
return make_string(cast(u8 const *)s, gb_string_length(s));
|
||||
}
|
||||
|
||||
gb_internal String lb_source_code_location_gen_name(lbProcedure *p, Ast *node) {
|
||||
String proc_name = {};
|
||||
if (p->entity) {
|
||||
proc_name = p->entity->token.string;
|
||||
}
|
||||
TokenPos pos = {};
|
||||
if (node) {
|
||||
pos = ast_token(node).pos;
|
||||
}
|
||||
return lb_source_code_location_gen_name(proc_name, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
|
||||
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_with_name(p->module, loc.type, loc, lb_source_code_location_gen_name(procedure, pos));
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_source_code_location_as_global_ptr(lbModule *m, String const &procedure, TokenPos const &pos) {
|
||||
lbValue loc = lb_const_source_code_location_const(m, procedure, pos);
|
||||
lbAddr addr = lb_add_global_generated(m, loc.type, loc, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_with_name(m, loc.type, loc, lb_source_code_location_gen_name(procedure, pos));
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
|
||||
lbValue loc = lb_emit_source_code_location_const(p, node);
|
||||
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_with_name(p->module, loc.type, loc, lb_source_code_location_gen_name(p, node));
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
|
||||
}
|
||||
@@ -562,12 +588,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
return lb_addr_load(p, slice);
|
||||
}
|
||||
} else {
|
||||
isize max_len = 7+8+1;
|
||||
char *str = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(str, max_len, "csba$%x", id);
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
gbString str = gb_string_make(temporary_allocator(), "csba$");
|
||||
str = gb_string_appendc(str, m->module_name);
|
||||
str = gb_string_append_fmt(str, "$%x", id);
|
||||
|
||||
String name = make_string(cast(u8 *)str, len-1);
|
||||
String name = make_string(cast(u8 const *)str, gb_string_length(str));
|
||||
|
||||
Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
|
||||
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
|
||||
|
||||
+13
-16
@@ -843,7 +843,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
8*cast(unsigned)type_align_of(type),
|
||||
lb_debug_type(m, type->EnumeratedArray.elem),
|
||||
subscripts, gb_count_of(subscripts));
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
gbString name = temp_canonical_string(type);
|
||||
return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
|
||||
}
|
||||
|
||||
@@ -852,16 +852,16 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
Type *bt = base_type(type->Map.debug_metadata_type);
|
||||
GB_ASSERT(bt->kind == Type_Struct);
|
||||
|
||||
return lb_debug_struct(m, type, bt, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
return lb_debug_struct(m, type, bt, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
case Type_Struct: return lb_debug_struct( m, type, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_Slice: return lb_debug_slice( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
|
||||
case Type_Struct: return lb_debug_struct( m, type, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_Slice: return lb_debug_slice( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_Union: return lb_debug_union( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_BitSet: return lb_debug_bitset( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_Enum: return lb_debug_enum( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
case Type_BitField: return lb_debug_bitfield( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
|
||||
|
||||
case Type_Tuple:
|
||||
if (type->Tuple.variables.count == 1) {
|
||||
@@ -904,7 +904,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
{
|
||||
LLVMMetadataRef proc_underlying_type = lb_debug_type_internal_proc(m, type);
|
||||
LLVMMetadataRef pointer_type = LLVMDIBuilderCreatePointerType(m->debug_builder, proc_underlying_type, ptr_bits, ptr_bits, 0, nullptr, 0);
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
gbString name = temp_canonical_string(type);
|
||||
return LLVMDIBuilderCreateTypedef(m->debug_builder, pointer_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
|
||||
}
|
||||
break;
|
||||
@@ -987,10 +987,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
|
||||
line = cast(unsigned)e->token.pos.line;
|
||||
}
|
||||
|
||||
String name = type->Named.name;
|
||||
if (type->Named.type_name && type->Named.type_name->pkg && type->Named.type_name->pkg->name.len != 0) {
|
||||
name = concatenate3_strings(temporary_allocator(), type->Named.type_name->pkg->name, str_lit("."), type->Named.name);
|
||||
}
|
||||
String name = type_to_canonical_string(temporary_allocator(), type);
|
||||
|
||||
Type *bt = base_type(type->Named.base);
|
||||
|
||||
@@ -1187,8 +1184,8 @@ gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx
|
||||
gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
|
||||
String name = e->token.string;
|
||||
if (e->pkg && e->pkg->name.len > 0) {
|
||||
// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
|
||||
name = concatenate3_strings(allocator, e->pkg->name, str_lit("::"), name);
|
||||
gbString s = string_canonical_entity_name(allocator, e);
|
||||
name = make_string(cast(u8 const *)s, gb_string_length(s));
|
||||
if (did_allocate_) *did_allocate_ = true;
|
||||
}
|
||||
return name;
|
||||
|
||||
@@ -1554,7 +1554,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
|
||||
lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right);
|
||||
Type *type = default_type(tv.type);
|
||||
return lb_emit_conv(p, cmp, type);
|
||||
} else if (lb_is_empty_string_constant(be->right)) {
|
||||
} else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) {
|
||||
// `x == ""` or `x != ""`
|
||||
lbValue s = lb_build_expr(p, be->left);
|
||||
s = lb_emit_conv(p, s, t_string);
|
||||
@@ -1562,7 +1562,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
|
||||
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
|
||||
Type *type = default_type(tv.type);
|
||||
return lb_emit_conv(p, cmp, type);
|
||||
} else if (lb_is_empty_string_constant(be->left)) {
|
||||
} else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) {
|
||||
// `"" == x` or `"" != x`
|
||||
lbValue s = lb_build_expr(p, be->right);
|
||||
s = lb_emit_conv(p, s, t_string);
|
||||
@@ -2312,9 +2312,9 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
lbValue array_const_value = {};
|
||||
array_const_value.type = t;
|
||||
array_const_value.value = LLVMConstArray(lb_type(m, elem), values, cast(unsigned)index_count);
|
||||
v = lb_add_global_generated(m, t, array_const_value);
|
||||
v = lb_add_global_generated_from_procedure(p, t, array_const_value);
|
||||
} else {
|
||||
v = lb_add_global_generated(m, t);
|
||||
v = lb_add_global_generated_from_procedure(p, t);
|
||||
}
|
||||
|
||||
lb_make_global_private_const(v);
|
||||
@@ -3004,7 +3004,16 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
|
||||
LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
|
||||
LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
|
||||
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
|
||||
|
||||
switch (op_kind) {
|
||||
case Token_CmpEq:
|
||||
res.value = LLVMBuildICmp(p->builder, LLVMIntEQ, mask_int, LLVMConstInt(mask_int_type, U64_MAX, true), "");
|
||||
break;
|
||||
case Token_NotEq:
|
||||
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(mask_int_type), "");
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
} else {
|
||||
@@ -3255,7 +3264,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
|
||||
Type *type = v.type;
|
||||
lbAddr addr = {};
|
||||
if (p->is_startup) {
|
||||
addr = lb_add_global_generated(p->module, type, v);
|
||||
addr = lb_add_global_generated_from_procedure(p, type, v);
|
||||
} else {
|
||||
addr = lb_add_local_generated(p, type, false);
|
||||
}
|
||||
@@ -3842,7 +3851,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
|
||||
Type *t = default_type(type_of_expr(expr));
|
||||
lbValue v = lb_const_value(p->module, t, e->Constant.value);
|
||||
if (LLVMIsConstant(v.value)) {
|
||||
lbAddr g = lb_add_global_generated(p->module, t, v);
|
||||
lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
|
||||
return g;
|
||||
}
|
||||
GB_ASSERT(LLVMIsALoadInst(v.value));
|
||||
|
||||
+120
-186
@@ -18,17 +18,36 @@ gb_global isize lb_global_type_info_member_tags_index = 0;
|
||||
gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
m->info = &c->info;
|
||||
|
||||
gbString module_name = gb_string_make(heap_allocator(), "odin_package");
|
||||
if (m->file) {
|
||||
module_name = gb_string_append_fmt(module_name, "-%u", m->file->id+1);
|
||||
|
||||
String name = build_context.build_paths[BuildPath_Output].name;
|
||||
gbString module_name = gb_string_make(heap_allocator(), "");
|
||||
module_name = gb_string_append_length(module_name, name.text, name.len);
|
||||
|
||||
if (!USE_SEPARATE_MODULES) {
|
||||
// ignore suffixes
|
||||
} else if (m->file) {
|
||||
if (gb_string_length(module_name)) {
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
}
|
||||
if (m->pkg) {
|
||||
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
}
|
||||
String filename = filename_from_path(m->file->filename);
|
||||
module_name = gb_string_append_length(module_name, filename.text, filename.len);
|
||||
} else if (m->pkg) {
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
if (gb_string_length(module_name)) {
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
}
|
||||
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
|
||||
} else if (USE_SEPARATE_MODULES) {
|
||||
module_name = gb_string_appendc(module_name, "-builtin");
|
||||
} else {
|
||||
if (gb_string_length(module_name)) {
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
}
|
||||
module_name = gb_string_appendc(module_name, "builtin");
|
||||
}
|
||||
|
||||
m->module_name = module_name ? module_name : "odin_package";
|
||||
m->module_name = module_name;
|
||||
m->ctx = LLVMContextCreate();
|
||||
m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx);
|
||||
// m->debug_builder = nullptr;
|
||||
@@ -67,10 +86,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
string_map_init(&m->procedures);
|
||||
string_map_init(&m->const_strings);
|
||||
map_init(&m->function_type_map);
|
||||
map_init(&m->equal_procs);
|
||||
map_init(&m->hasher_procs);
|
||||
map_init(&m->map_get_procs);
|
||||
map_init(&m->map_set_procs);
|
||||
string_map_init(&m->gen_procs);
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
array_init(&m->procedures_to_generate, a, 0, 1<<10);
|
||||
map_init(&m->procedure_values, 1<<11);
|
||||
@@ -218,7 +234,7 @@ gb_internal void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
|
||||
|
||||
gb_internal void lb_make_global_private_const(LLVMValueRef global_data) {
|
||||
LLVMSetLinkage(global_data, LLVMLinkerPrivateLinkage);
|
||||
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
|
||||
// LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetGlobalConstant(global_data, true);
|
||||
}
|
||||
gb_internal void lb_make_global_private_const(lbAddr const &addr) {
|
||||
@@ -1010,7 +1026,7 @@ gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
|
||||
return;
|
||||
} else if (LLVMIsConstant(value.value)) {
|
||||
lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_from_procedure(p, value.type, value);
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
LLVMValueRef dst_ptr = ptr.value;
|
||||
@@ -1443,148 +1459,30 @@ gb_internal void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
|
||||
LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
|
||||
}
|
||||
|
||||
gb_internal String lb_mangle_name(Entity *e) {
|
||||
String name = e->token.string;
|
||||
|
||||
AstPackage *pkg = e->pkg;
|
||||
GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
|
||||
String pkgn = pkg->name;
|
||||
GB_ASSERT(!rune_is_digit(pkgn[0]));
|
||||
if (pkgn == "llvm") {
|
||||
pkgn = str_lit("llvm$");
|
||||
}
|
||||
|
||||
isize max_len = pkgn.len + 1 + name.len + 1;
|
||||
bool require_suffix_id = is_type_polymorphic(e->type, true);
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
|
||||
require_suffix_id = true;
|
||||
} else if (is_blank_ident(e->token)) {
|
||||
require_suffix_id = true;
|
||||
}if (e->flags & EntityFlag_NotExported) {
|
||||
require_suffix_id = true;
|
||||
}
|
||||
|
||||
if (require_suffix_id) {
|
||||
max_len += 21;
|
||||
}
|
||||
|
||||
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
isize new_name_len = gb_snprintf(
|
||||
new_name, max_len,
|
||||
"%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
|
||||
);
|
||||
if (require_suffix_id) {
|
||||
char *str = new_name + new_name_len-1;
|
||||
isize len = max_len-new_name_len;
|
||||
isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
|
||||
new_name_len += extra-1;
|
||||
}
|
||||
|
||||
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
|
||||
return mangled_name;
|
||||
}
|
||||
|
||||
gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p, lbModule *module) {
|
||||
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
|
||||
// and as a result, the declaration does not have time to determine what it should be
|
||||
|
||||
GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
|
||||
if (e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
|
||||
|
||||
if (p == nullptr) {
|
||||
Entity *proc = nullptr;
|
||||
if (e->parent_proc_decl != nullptr) {
|
||||
proc = e->parent_proc_decl->entity;
|
||||
} else {
|
||||
Scope *scope = e->scope;
|
||||
while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
|
||||
scope = scope->parent;
|
||||
}
|
||||
GB_ASSERT(scope != nullptr);
|
||||
GB_ASSERT(scope->flags & ScopeFlag_Proc);
|
||||
proc = scope->procedure_entity;
|
||||
}
|
||||
if (proc != nullptr) {
|
||||
GB_ASSERT(proc->kind == Entity_Procedure);
|
||||
if (proc->code_gen_procedure != nullptr) {
|
||||
p = proc->code_gen_procedure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
String ts_name = e->token.string;
|
||||
|
||||
if (p != nullptr) {
|
||||
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
|
||||
name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
} else {
|
||||
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
|
||||
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
static std::atomic<u32> guid;
|
||||
name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e) {
|
||||
GB_ASSERT(m != nullptr);
|
||||
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len != 0) {
|
||||
return e->Procedure.link_name;
|
||||
}
|
||||
|
||||
if (e->pkg == nullptr) {
|
||||
return e->token.string;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
|
||||
return lb_set_nested_type_name_ir_mangled_name(e, nullptr, m);
|
||||
}
|
||||
gbString w = string_canonical_entity_name(heap_allocator(), e);
|
||||
defer (gb_string_free(w));
|
||||
|
||||
String name = {};
|
||||
|
||||
bool no_name_mangle = false;
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
return e->Variable.link_name;
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
|
||||
return e->Procedure.link_name;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
|
||||
no_name_mangle = true;
|
||||
}
|
||||
|
||||
if (!no_name_mangle) {
|
||||
name = lb_mangle_name(e);
|
||||
}
|
||||
if (name.len == 0) {
|
||||
name = e->token.string;
|
||||
}
|
||||
String name = copy_string(permanent_allocator(), make_string(cast(u8 const *)w, gb_string_length(w)));
|
||||
|
||||
if (e->kind == Entity_TypeName) {
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
e->Procedure.link_name = name;
|
||||
} else if (e->kind == Entity_Variable) {
|
||||
e->Variable.link_name = name;
|
||||
}
|
||||
|
||||
return name;
|
||||
@@ -1902,15 +1800,24 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
return type;
|
||||
}
|
||||
type = LLVMStructCreateNamed(ctx, name);
|
||||
LLVMTypeRef fields[2] = {
|
||||
lb_type(m, t_rawptr),
|
||||
lb_type(m, t_typeid),
|
||||
};
|
||||
LLVMStructSetBody(type, fields, 2, false);
|
||||
if (build_context.ptr_size == 4) {
|
||||
LLVMTypeRef fields[3] = {
|
||||
lb_type(m, t_rawptr),
|
||||
lb_type_padding_filler(m, build_context.ptr_size, build_context.ptr_size), // padding
|
||||
lb_type(m, t_typeid),
|
||||
};
|
||||
LLVMStructSetBody(type, fields, 3, false);
|
||||
} else {
|
||||
LLVMTypeRef fields[2] = {
|
||||
lb_type(m, t_rawptr),
|
||||
lb_type(m, t_typeid),
|
||||
};
|
||||
LLVMStructSetBody(type, fields, 2, false);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)build_context.ptr_size);
|
||||
case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 64);
|
||||
|
||||
// Endian Specific Types
|
||||
case Basic_i16le: return LLVMInt16TypeInContext(ctx);
|
||||
@@ -2088,26 +1995,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
case Type_Struct:
|
||||
{
|
||||
type_set_offsets(type);
|
||||
|
||||
|
||||
i64 full_type_size = type_size_of(type);
|
||||
i64 full_type_align = type_align_of(type);
|
||||
GB_ASSERT(full_type_size % full_type_align == 0);
|
||||
|
||||
|
||||
if (type->Struct.is_raw_union) {
|
||||
|
||||
|
||||
lbStructFieldRemapping field_remapping = {};
|
||||
slice_init(&field_remapping, permanent_allocator(), 1);
|
||||
|
||||
|
||||
LLVMTypeRef fields[1] = {};
|
||||
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
|
||||
field_remapping[0] = 0;
|
||||
|
||||
|
||||
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
|
||||
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
|
||||
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
|
||||
return struct_type;
|
||||
}
|
||||
|
||||
|
||||
lbStructFieldRemapping field_remapping = {};
|
||||
slice_init(&field_remapping, permanent_allocator(), type->Struct.fields.count);
|
||||
|
||||
@@ -2120,7 +2027,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, type_align_of(type));
|
||||
array_add(&fields, padding_type);
|
||||
}
|
||||
|
||||
|
||||
i64 prev_offset = 0;
|
||||
bool requires_packing = type->Struct.is_packed;
|
||||
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
|
||||
@@ -2151,7 +2058,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
|
||||
prev_offset = offset + type_size_of(field->type);
|
||||
}
|
||||
|
||||
|
||||
i64 end_padding = full_type_size-prev_offset;
|
||||
if (end_padding > 0) {
|
||||
array_add(&fields, lb_type_padding_filler(m, end_padding, 1));
|
||||
@@ -2160,14 +2067,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
for_array(i, fields) {
|
||||
GB_ASSERT(fields[i] != nullptr);
|
||||
}
|
||||
|
||||
|
||||
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing);
|
||||
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
|
||||
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
|
||||
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
|
||||
#if 0
|
||||
GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
|
||||
"(%lld) %s vs (%lld) %s",
|
||||
cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
|
||||
GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
|
||||
"(%lld) %s vs (%lld) %s",
|
||||
cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
|
||||
cast(long long)full_type_size, type_to_string(type));
|
||||
#endif
|
||||
return struct_type;
|
||||
@@ -2196,8 +2103,22 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
|
||||
array_add(&fields, variant);
|
||||
} else {
|
||||
LLVMTypeRef block_type = lb_type_padding_filler(m, block_size, align);
|
||||
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
|
||||
LLVMTypeRef block_type = nullptr;
|
||||
|
||||
bool all_pointers = align == build_context.ptr_size;
|
||||
for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (!is_type_internally_pointer_like(t)) {
|
||||
all_pointers = false;
|
||||
}
|
||||
}
|
||||
if (all_pointers) {
|
||||
block_type = lb_type(m, t_rawptr);
|
||||
} else {
|
||||
block_type = lb_type_padding_filler(m, block_size, align);
|
||||
}
|
||||
|
||||
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
|
||||
array_add(&fields, block_type);
|
||||
array_add(&fields, tag_type);
|
||||
i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
|
||||
@@ -2633,12 +2554,10 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co
|
||||
false);
|
||||
|
||||
|
||||
isize max_len = 7+8+1;
|
||||
char *name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||
len -= 1;
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
gbString name = gb_string_make(temporary_allocator(), "csbs$");
|
||||
name = gb_string_appendc(name, m->module_name);
|
||||
name = gb_string_append_fmt(name, "$%x", id);
|
||||
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
@@ -2676,14 +2595,11 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *
|
||||
false);
|
||||
|
||||
|
||||
char *name = nullptr;
|
||||
{
|
||||
isize max_len = 7+8+1;
|
||||
name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||
len -= 1;
|
||||
}
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
gbString name = gb_string_make(temporary_allocator(), "csba$");
|
||||
name = gb_string_appendc(name, m->module_name);
|
||||
name = gb_string_append_fmt(name, "$%x", id);
|
||||
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
LLVMSetInitializer(global_data, data);
|
||||
@@ -2869,6 +2785,8 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
|
||||
pl->decl->code_gen_module = m;
|
||||
e->decl_info = pl->decl;
|
||||
pl->decl->entity = e;
|
||||
e->parent_proc_decl = pl->decl->parent;
|
||||
e->Procedure.is_anonymous = true;
|
||||
e->flags |= EntityFlag_ProcBodyChecked;
|
||||
|
||||
lbProcedure *p = lb_create_procedure(m, e);
|
||||
@@ -2889,17 +2807,14 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
|
||||
gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lbValue value, String name, Entity **entity_) {
|
||||
GB_ASSERT(name.len != 0);
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = default_type(type);
|
||||
|
||||
isize max_len = 7+8+1;
|
||||
u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
|
||||
|
||||
u32 id = m->gen->global_generated_index.fetch_add(1);
|
||||
|
||||
isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", id);
|
||||
String name = make_string(str, len-1);
|
||||
u8 *str = cast(u8 *)gb_alloc_array(temporary_allocator(), u8, name.len);
|
||||
memcpy(str, name.text, name.len);
|
||||
str[name.len] = 0;
|
||||
|
||||
Scope *scope = nullptr;
|
||||
Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
|
||||
@@ -2921,6 +2836,25 @@ gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue valu
|
||||
return lb_addr(g);
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbAddr lb_add_global_generated_from_procedure(lbProcedure *p, Type *type, lbValue value) {
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = default_type(type);
|
||||
|
||||
u32 index = ++p->global_generated_index;
|
||||
|
||||
gbString s = gb_string_make(temporary_allocator(), "ggv$");
|
||||
// s = gb_string_appendc(s, p->module->module_name);
|
||||
// s = gb_string_appendc(s, "$");
|
||||
s = gb_string_append_length(s, p->name.text, p->name.len);
|
||||
s = gb_string_append_fmt(s, "$%u", index);
|
||||
|
||||
String name = make_string(cast(u8 const *)s, gb_string_length(s));
|
||||
return lb_add_global_generated_with_name(p->module, type, value, name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_find_runtime_value(lbModule *m, String const &name) {
|
||||
AstPackage *p = m->info->runtime_package;
|
||||
Entity *e = scope_lookup_current(p->scope, name);
|
||||
@@ -3028,7 +2962,7 @@ gb_internal lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 c
|
||||
g.type = alloc_type_pointer(t);
|
||||
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
|
||||
LLVMSetLinkage(g.value, LLVMPrivateLinkage);
|
||||
LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
|
||||
// LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
|
||||
string_map_set(&m->members, s, g);
|
||||
return g;
|
||||
}
|
||||
|
||||
@@ -1119,7 +1119,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
|
||||
if (LLVMIsConstant(x.value)) {
|
||||
// NOTE(bill): if the value is already constant, then just it as a global variable
|
||||
// and pass it by pointer
|
||||
lbAddr addr = lb_add_global_generated(p->module, original_type, x);
|
||||
lbAddr addr = lb_add_global_generated_from_procedure(p, original_type, x);
|
||||
lb_make_global_private_const(addr);
|
||||
ptr = addr.addr;
|
||||
} else {
|
||||
@@ -1564,6 +1564,34 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_simd_extract_lsbs:
|
||||
case BuiltinProc_simd_extract_msbs:
|
||||
{
|
||||
Type *vt = arg0.type;
|
||||
GB_ASSERT(vt->kind == Type_SimdVector);
|
||||
|
||||
i64 elem_bits = 8*type_size_of(elem);
|
||||
i64 num_elems = get_array_type_count(vt);
|
||||
|
||||
LLVMValueRef broadcast_value = arg0.value;
|
||||
if (builtin_id == BuiltinProc_simd_extract_msbs) {
|
||||
LLVMTypeRef word_type = lb_type(m, elem);
|
||||
LLVMValueRef shift_value = llvm_splat_int(num_elems, word_type, elem_bits - 1);
|
||||
broadcast_value = LLVMBuildAShr(p->builder, broadcast_value, shift_value, "");
|
||||
}
|
||||
|
||||
LLVMTypeRef bitvec_type = LLVMVectorType(LLVMInt1TypeInContext(m->ctx), (unsigned)num_elems);
|
||||
LLVMValueRef bitvec_value = LLVMBuildTrunc(p->builder, broadcast_value, bitvec_type, "");
|
||||
|
||||
LLVMTypeRef mask_type = LLVMIntTypeInContext(m->ctx, (unsigned)num_elems);
|
||||
LLVMValueRef mask_value = LLVMBuildBitCast(p->builder, bitvec_value, mask_type, "");
|
||||
|
||||
LLVMTypeRef result_type = lb_type(m, res.type);
|
||||
res.value = LLVMBuildZExtOrBitCast(p->builder, mask_value, result_type, "");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
case BuiltinProc_simd_shuffle:
|
||||
{
|
||||
@@ -1846,7 +1874,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count);
|
||||
|
||||
Type *array_type = alloc_type_array(t_load_directory_file, count);
|
||||
lbAddr backing_array_addr = lb_add_global_generated(m, array_type, {backing_array, array_type}, nullptr);
|
||||
lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type});
|
||||
lb_make_global_private_const(backing_array_addr);
|
||||
|
||||
LLVMValueRef backing_array_ptr = backing_array_addr.addr.value;
|
||||
@@ -1854,7 +1882,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
|
||||
LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false));
|
||||
|
||||
lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr);
|
||||
lbAddr addr = lb_add_global_generated_from_procedure(p, tv.type, {const_slice, t_load_directory_file_slice});
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
return lb_addr_load(p, addr);
|
||||
@@ -2548,8 +2576,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
}
|
||||
case BuiltinProc_ptr_sub:
|
||||
{
|
||||
Type *elem0 = type_deref(type_of_expr(ce->args[0]));
|
||||
Type *elem1 = type_deref(type_of_expr(ce->args[1]));
|
||||
Type *elem0 = type_deref(type_of_expr(ce->args[0]), true);
|
||||
Type *elem1 = type_deref(type_of_expr(ce->args[1]), true);
|
||||
GB_ASSERT(are_types_identical(elem0, elem1));
|
||||
Type *elem = elem0;
|
||||
|
||||
@@ -3280,13 +3308,14 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
{
|
||||
isize max_len = 7+8+1;
|
||||
name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||
len -= 1;
|
||||
}
|
||||
LLVMTypeRef type = LLVMTypeOf(array);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
LLVMSetInitializer(global_data, array);
|
||||
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetLinkage(global_data, LLVMInternalLinkage);
|
||||
|
||||
|
||||
|
||||
+36
-56
@@ -5,8 +5,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
|
||||
auto *min_dep_set = &p->module->info->minimum_dependency_set;
|
||||
|
||||
static i32 global_guid = 0;
|
||||
|
||||
for (Ast *ident : vd->names) {
|
||||
GB_ASSERT(ident->kind == Ast_Ident);
|
||||
Entity *e = entity_of_node(ident);
|
||||
@@ -32,7 +30,8 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
continue;
|
||||
}
|
||||
|
||||
lb_set_nested_type_name_ir_mangled_name(e, p, p->module);
|
||||
String name = lb_get_entity_name(p->module, e);
|
||||
gb_unused(name);
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
@@ -1115,62 +1114,43 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
|
||||
}
|
||||
|
||||
Type *elem = et->BitSet.elem;
|
||||
if (is_type_enum(elem)) {
|
||||
i64 enum_count = 0;
|
||||
lbValue values = lb_enum_values_slice(p, elem, &enum_count);
|
||||
lbValue values_data = lb_slice_elem(p, values);
|
||||
lbValue max_count = lb_const_int(m, t_int, enum_count);
|
||||
Type *mask = bit_set_to_int(et);
|
||||
|
||||
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
|
||||
lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
|
||||
lbValue all_mask = lb_const_value(p->module, mask, exact_bit_set_all_set_mask(et));
|
||||
lbValue initial_mask = lb_emit_arith(p, Token_And, the_set, all_mask, mask);
|
||||
|
||||
loop = lb_create_block(p, "for.bit_set.enum.loop");
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
lbBlock *body_check = lb_create_block(p, "for.bit_set.enum.body-check");
|
||||
lbBlock *body = lb_create_block(p, "for.bit_set.enum.body");
|
||||
done = lb_create_block(p, "for.bit_set.enum.done");
|
||||
|
||||
lbValue offset = lb_addr_load(p, offset_);
|
||||
lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count);
|
||||
lb_emit_if(p, cond, body_check, done);
|
||||
lb_start_block(p, body_check);
|
||||
|
||||
lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset);
|
||||
lb_emit_increment(p, offset_.addr);
|
||||
val = lb_emit_load(p, val_ptr);
|
||||
val = lb_emit_conv(p, val, elem);
|
||||
|
||||
lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
|
||||
lb_emit_if(p, check, body, loop);
|
||||
lb_start_block(p, body);
|
||||
} else {
|
||||
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
|
||||
lb_addr_store(p, offset_, lb_const_int(m, t_int, et->BitSet.lower));
|
||||
|
||||
lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper);
|
||||
|
||||
loop = lb_create_block(p, "for.bit_set.range.loop");
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
lbBlock *body_check = lb_create_block(p, "for.bit_set.range.body-check");
|
||||
lbBlock *body = lb_create_block(p, "for.bit_set.range.body");
|
||||
done = lb_create_block(p, "for.bit_set.range.done");
|
||||
|
||||
lbValue offset = lb_addr_load(p, offset_);
|
||||
lbValue cond = lb_emit_comp(p, Token_LtEq, offset, max_count);
|
||||
lb_emit_if(p, cond, body_check, done);
|
||||
lb_start_block(p, body_check);
|
||||
|
||||
val = lb_emit_conv(p, offset, elem);
|
||||
lb_emit_increment(p, offset_.addr);
|
||||
|
||||
lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
|
||||
lb_emit_if(p, check, body, loop);
|
||||
lb_start_block(p, body);
|
||||
if (rs->reverse) {
|
||||
initial_mask = lb_emit_reverse_bits(p, initial_mask, mask);
|
||||
}
|
||||
|
||||
lbAddr remaining = lb_add_local_generated(p, mask, false);
|
||||
lb_addr_store(p, remaining, initial_mask);
|
||||
|
||||
loop = lb_create_block(p, "for.bit_set.loop");
|
||||
lbBlock *body = lb_create_block(p, "for.bit_set.body");
|
||||
done = lb_create_block(p, "for.bit_set.done");
|
||||
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
lbValue remaining_val = lb_addr_load(p, remaining);
|
||||
lbValue cond = lb_emit_comp(p, Token_NotEq, remaining_val, lb_zero(m, mask));
|
||||
lb_emit_if(p, cond, body, done);
|
||||
|
||||
lb_start_block(p, body);
|
||||
val = lb_emit_count_trailing_zeros(p, remaining_val, mask);
|
||||
val = lb_emit_conv(p, val, elem);
|
||||
|
||||
if (rs->reverse) {
|
||||
val = lb_emit_arith(p, Token_Sub, lb_const_int(m, elem, et->BitSet.lower + 8*type_size_of(mask) - 1), val, elem);
|
||||
} else {
|
||||
val = lb_emit_arith(p, Token_Add, val, lb_const_int(m, elem, et->BitSet.lower), elem);
|
||||
}
|
||||
|
||||
lbValue reduce_val = lb_emit_arith(p, Token_Sub, remaining_val, lb_const_int(m, mask, 1), mask);
|
||||
remaining_val = lb_emit_arith(p, Token_And, remaining_val, reduce_val, mask);
|
||||
lb_addr_store(p, remaining, remaining_val);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
+25
-48
@@ -1,24 +1,24 @@
|
||||
gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
|
||||
auto *set = &info->minimum_dependency_type_info_set;
|
||||
isize index = type_info_index(info, type, err_on_not_found);
|
||||
|
||||
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
|
||||
isize index = type_info_index(info, pair, err_on_not_found);
|
||||
if (index >= 0) {
|
||||
auto *found = map_get(set, index+1);
|
||||
if (found) {
|
||||
GB_ASSERT(*found >= 0);
|
||||
return *found + 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
if (err_on_not_found) {
|
||||
gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(type), index, set->count);
|
||||
for (auto const &entry : *set) {
|
||||
gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(pair.type), index, info->min_dep_type_info_index_map.count);
|
||||
for (auto const &entry : info->min_dep_type_info_index_map) {
|
||||
isize type_info_index = entry.key;
|
||||
gb_printf_err("\t%s\n", type_to_string(info->type_info_types[type_info_index]));
|
||||
gb_printf_err("\t%s\n", type_to_string(info->type_info_types_hash_map[type_info_index].type));
|
||||
}
|
||||
GB_PANIC("NOT FOUND");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
|
||||
return lb_type_info_index(info, {type, type_hash_canonical_type(type)}, err_on_not_found);
|
||||
}
|
||||
|
||||
gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
|
||||
GB_ASSERT(!build_context.no_rtti);
|
||||
|
||||
@@ -73,37 +73,8 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
|
||||
|
||||
type = default_type(type);
|
||||
|
||||
u64 id = cast(u64)lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(id >= 0);
|
||||
|
||||
u64 kind = lb_typeid_kind(m, type, id);
|
||||
u64 named = is_type_named(type) && type->kind != Type_Basic;
|
||||
u64 special = 0;
|
||||
u64 reserved = 0;
|
||||
|
||||
if (is_type_cstring(type)) {
|
||||
special = 1;
|
||||
} else if (is_type_integer(type) && !is_type_unsigned(type)) {
|
||||
special = 1;
|
||||
}
|
||||
|
||||
u64 data = 0;
|
||||
if (build_context.ptr_size == 4) {
|
||||
GB_ASSERT(id <= (1u<<24u));
|
||||
data |= (id &~ (1u<<24)) << 0u; // index
|
||||
data |= (kind &~ (1u<<5)) << 24u; // kind
|
||||
data |= (named &~ (1u<<1)) << 29u; // named
|
||||
data |= (special &~ (1u<<1)) << 30u; // special
|
||||
data |= (reserved &~ (1u<<1)) << 31u; // reserved
|
||||
} else {
|
||||
GB_ASSERT(build_context.ptr_size == 8);
|
||||
GB_ASSERT(id <= (1ull<<56u));
|
||||
data |= (id &~ (1ull<<56)) << 0ul; // index
|
||||
data |= (kind &~ (1ull<<5)) << 56ull; // kind
|
||||
data |= (named &~ (1ull<<1)) << 61ull; // named
|
||||
data |= (special &~ (1ull<<1)) << 62ull; // special
|
||||
data |= (reserved &~ (1ull<<1)) << 63ull; // reserved
|
||||
}
|
||||
u64 data = type_hash_canonical_type(type);
|
||||
GB_ASSERT(data != 0);
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMConstInt(lb_type(m, t_typeid), data, false);
|
||||
@@ -279,13 +250,14 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
|
||||
LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count);
|
||||
defer (gb_free(heap_allocator(), modified_types));
|
||||
for_array(type_info_type_index, info->type_info_types) {
|
||||
Type *t = info->type_info_types[type_info_type_index];
|
||||
for_array(type_info_type_index, info->type_info_types_hash_map) {
|
||||
auto const &tt = info->type_info_types_hash_map[type_info_type_index];
|
||||
Type *t = tt.type;
|
||||
if (t == nullptr || t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
isize entry_index = lb_type_info_index(info, t, false);
|
||||
isize entry_index = lb_type_info_index(info, tt, false);
|
||||
if (entry_index <= 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -342,8 +314,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
return giant_const_values[index];
|
||||
};
|
||||
|
||||
for_array(type_info_type_index, info->type_info_types) {
|
||||
Type *t = info->type_info_types[type_info_type_index];
|
||||
for_array(type_info_type_index, info->type_info_types_hash_map) {
|
||||
Type *t = info->type_info_types_hash_map[type_info_type_index].type;
|
||||
if (t == nullptr || t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
@@ -1071,7 +1043,12 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1));
|
||||
}
|
||||
for (isize i = 0; i < global_type_info_data_entity_count; i++) {
|
||||
giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr));
|
||||
auto *ptr = &giant_const_values[i];
|
||||
if (*ptr != nullptr) {
|
||||
*ptr = LLVMConstPointerCast(*ptr, lb_type(m, t_type_info_ptr));
|
||||
} else {
|
||||
*ptr = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -971,6 +971,13 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
|
||||
if (t->kind == Type_Struct) {
|
||||
auto field_remapping = lb_get_struct_remapping(m, t);
|
||||
return field_remapping[index];
|
||||
} else if (is_type_any(t) && build_context.ptr_size == 4) {
|
||||
GB_ASSERT(t->kind == Type_Basic);
|
||||
GB_ASSERT(t->Basic.kind == Basic_any);
|
||||
switch (index) {
|
||||
case 0: return 0; // data
|
||||
case 1: return 2; // id
|
||||
}
|
||||
} else if (build_context.ptr_size != build_context.int_size) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
@@ -2105,7 +2112,13 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &entity);
|
||||
gbString global_name = gb_string_make(temporary_allocator(), "__$objc_SEL$");
|
||||
global_name = gb_string_append_length(global_name, name.text, name.len);
|
||||
|
||||
lbAddr default_addr = lb_add_global_generated_with_name(
|
||||
default_module, t_objc_SEL, {},
|
||||
make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
|
||||
&entity);
|
||||
string_map_set(&default_module->objc_selectors, name, lbObjcRef{entity, default_addr});
|
||||
}
|
||||
|
||||
@@ -2162,7 +2175,12 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &entity);
|
||||
gbString global_name = gb_string_make(temporary_allocator(), "__$objc_Class$");
|
||||
global_name = gb_string_append_length(global_name, name.text, name.len);
|
||||
|
||||
lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_Class, {},
|
||||
make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
|
||||
&entity);
|
||||
string_map_set(&default_module->objc_classes, name, lbObjcRef{entity, default_addr});
|
||||
}
|
||||
|
||||
|
||||
@@ -3593,10 +3593,15 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
if (build_context.generate_docs) {
|
||||
MAIN_TIME_SECTION("generate documentation");
|
||||
if (global_error_collector.count != 0) {
|
||||
return 1;
|
||||
}
|
||||
generate_documentation(checker);
|
||||
|
||||
if (build_context.show_timings) {
|
||||
show_timings(checker, &global_timings);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,752 @@
|
||||
gb_internal GB_COMPARE_PROC(type_info_pair_cmp) {
|
||||
TypeInfoPair *x = cast(TypeInfoPair *)a;
|
||||
TypeInfoPair *y = cast(TypeInfoPair *)b;
|
||||
if (x->hash == y->hash) {
|
||||
return 0;
|
||||
}
|
||||
return x->hash < y->hash ? -1 : +1;
|
||||
}
|
||||
|
||||
|
||||
gb_internal gbAllocator type_set_allocator(void) {
|
||||
return heap_allocator();
|
||||
}
|
||||
|
||||
gb_internal TypeSetIterator begin(TypeSet &set) noexcept {
|
||||
usize index = 0;
|
||||
while (index < set.capacity) {
|
||||
TypeInfoPair key = set.keys[index];
|
||||
if (key.hash != 0 && key.hash != TYPE_SET_TOMBSTONE) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return TypeSetIterator{&set, index};
|
||||
}
|
||||
gb_internal TypeSetIterator end(TypeSet &set) noexcept {
|
||||
return TypeSetIterator{&set, set.capacity};
|
||||
}
|
||||
|
||||
|
||||
gb_internal void type_set_init(TypeSet *s, isize capacity) {
|
||||
GB_ASSERT(s->keys == nullptr);
|
||||
if (capacity != 0) {
|
||||
capacity = next_pow2_isize(gb_max(16, capacity));
|
||||
s->keys = gb_alloc_array(type_set_allocator(), TypeInfoPair, capacity);
|
||||
// This memory will be zeroed, no need to explicitly zero it
|
||||
}
|
||||
s->count = 0;
|
||||
s->capacity = capacity;
|
||||
}
|
||||
|
||||
gb_internal void type_set_destroy(TypeSet *s) {
|
||||
gb_free(type_set_allocator(), s->keys);
|
||||
s->keys = nullptr;
|
||||
s->count = 0;
|
||||
s->capacity = 0;
|
||||
}
|
||||
|
||||
|
||||
gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) {
|
||||
GB_ASSERT(pair.type != nullptr);
|
||||
GB_ASSERT(pair.hash != 0);
|
||||
if (s->count != 0) {
|
||||
usize hash = pair.hash;
|
||||
usize mask = s->capacity-1;
|
||||
usize hash_index = cast(usize)hash & mask;
|
||||
for (usize i = 0; i < s->capacity; i++) {
|
||||
Type *key = s->keys[hash_index].type;
|
||||
if (are_types_identical_unique_tuples(key, pair.type)) {
|
||||
return hash_index;
|
||||
} else if (key == 0) {
|
||||
return -1;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
gb_internal isize type_set__find(TypeSet *s, Type *ptr) {
|
||||
GB_ASSERT(ptr != 0);
|
||||
if (s->count != 0) {
|
||||
usize hash = cast(usize)type_hash_canonical_type(ptr);
|
||||
usize mask = s->capacity-1;
|
||||
usize hash_index = cast(usize)hash & mask;
|
||||
for (usize i = 0; i < s->capacity; i++) {
|
||||
Type *key = s->keys[hash_index].type;
|
||||
if (are_types_identical_unique_tuples(key, ptr)) {
|
||||
return hash_index;
|
||||
} else if (key == 0) {
|
||||
return -1;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
gb_internal bool type_set__full(TypeSet *s) {
|
||||
return 0.75f * s->capacity <= s->count;
|
||||
}
|
||||
|
||||
gb_internal gb_inline void type_set_grow(TypeSet *old_set) {
|
||||
if (old_set->capacity == 0) {
|
||||
type_set_init(old_set);
|
||||
return;
|
||||
}
|
||||
|
||||
TypeSet new_set = {};
|
||||
type_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
|
||||
|
||||
for (TypeInfoPair const &set : *old_set) {
|
||||
bool was_new = type_set_update(&new_set, set);
|
||||
GB_ASSERT(!was_new);
|
||||
}
|
||||
GB_ASSERT(old_set->count == new_set.count);
|
||||
|
||||
type_set_destroy(old_set);
|
||||
|
||||
*old_set = new_set;
|
||||
}
|
||||
|
||||
|
||||
gb_internal gb_inline bool type_set_exists(TypeSet *s, Type *ptr) {
|
||||
return type_set__find(s, ptr) >= 0;
|
||||
}
|
||||
gb_internal gb_inline bool type_set_exists(TypeSet *s, TypeInfoPair pair) {
|
||||
return type_set__find(s, pair) >= 0;
|
||||
}
|
||||
gb_internal gb_inline TypeInfoPair *type_set_retrieve(TypeSet *s, Type *type) {
|
||||
isize index = type_set__find(s, type);
|
||||
if (index >= 0) {
|
||||
return &s->keys[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool type_set_update(TypeSet *s, TypeInfoPair pair) { // returns true if it previously existsed
|
||||
if (type_set_exists(s, pair)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s->keys == nullptr) {
|
||||
type_set_init(s);
|
||||
} else if (type_set__full(s)) {
|
||||
type_set_grow(s);
|
||||
}
|
||||
GB_ASSERT(s->count < s->capacity);
|
||||
GB_ASSERT(s->capacity >= 0);
|
||||
|
||||
usize mask = s->capacity-1;
|
||||
usize hash = cast(usize)pair.hash;
|
||||
usize hash_index = (cast(usize)hash) & mask;
|
||||
GB_ASSERT(hash_index < s->capacity);
|
||||
for (usize i = 0; i < s->capacity; i++) {
|
||||
TypeInfoPair *key = &s->keys[hash_index];
|
||||
GB_ASSERT(!are_types_identical_unique_tuples(key->type, pair.type));
|
||||
if (key->hash == TYPE_SET_TOMBSTONE || key->hash == 0) {
|
||||
*key = pair;
|
||||
s->count++;
|
||||
return false;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
}
|
||||
|
||||
GB_PANIC("ptr set out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it previously existsed
|
||||
TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)};
|
||||
return type_set_update(s, pair);
|
||||
}
|
||||
|
||||
|
||||
gb_internal Type *type_set_add(TypeSet *s, Type *ptr) {
|
||||
type_set_update(s, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
gb_internal Type *type_set_add(TypeSet *s, TypeInfoPair pair) {
|
||||
type_set_update(s, pair);
|
||||
return pair.type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal void type_set_remove(TypeSet *s, Type *ptr) {
|
||||
isize index = type_set__find(s, ptr);
|
||||
if (index >= 0) {
|
||||
GB_ASSERT(s->count > 0);
|
||||
s->keys[index].type = nullptr;
|
||||
s->keys[index].hash = TYPE_SET_TOMBSTONE;
|
||||
s->count--;
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal gb_inline void type_set_clear(TypeSet *s) {
|
||||
s->count = 0;
|
||||
gb_zero_size(s->keys, s->capacity*gb_size_of(*s->keys));
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_WRITER_PROC(name) bool name(TypeWriter *w, void const *ptr, isize len)
|
||||
typedef TYPE_WRITER_PROC(TypeWriterProc);
|
||||
|
||||
|
||||
struct TypeWriter {
|
||||
TypeWriterProc *proc;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
bool type_writer_append(TypeWriter *w, void const *ptr, isize len) {
|
||||
return w->proc(w, ptr, len);
|
||||
}
|
||||
|
||||
bool type_writer_appendb(TypeWriter *w, char b) {
|
||||
return w->proc(w, &b, 1);
|
||||
}
|
||||
|
||||
bool type_writer_appendc(TypeWriter *w, char const *str) {
|
||||
isize len = gb_strlen(str);
|
||||
return w->proc(w, str, len);
|
||||
}
|
||||
|
||||
bool type_writer_append_fmt(TypeWriter *w, char const *fmt, ...) {
|
||||
va_list va;
|
||||
char *str;
|
||||
va_start(va, fmt);
|
||||
str = gb_bprintf_va(fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return type_writer_appendc(w, str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TYPE_WRITER_PROC(type_writer_string_writer_proc) {
|
||||
gbString *s = cast(gbString *)&w->user_data;
|
||||
*s = gb_string_append_length(*s, ptr, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
void type_writer_make_string(TypeWriter *w, gbAllocator allocator) {
|
||||
w->user_data = gb_string_make(allocator, "");
|
||||
w->proc = type_writer_string_writer_proc;
|
||||
}
|
||||
|
||||
void type_writer_destroy_string(TypeWriter *w) {
|
||||
gb_string_free(cast(gbString)w->user_data);
|
||||
}
|
||||
|
||||
|
||||
TYPE_WRITER_PROC(type_writer_hasher_writer_proc) {
|
||||
u64 *seed = cast(u64 *)w->user_data;
|
||||
*seed = fnv64a(ptr, len, *seed);
|
||||
return true;
|
||||
}
|
||||
|
||||
void type_writer_make_hasher(TypeWriter *w, u64 *hash) {
|
||||
w->user_data = hash;
|
||||
w->proc = type_writer_hasher_writer_proc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal void write_canonical_params(TypeWriter *w, Type *params) {
|
||||
type_writer_appendc(w, "(");
|
||||
defer (type_writer_appendc(w, ")"));
|
||||
|
||||
if (params == nullptr) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(params->kind == Type_Tuple);
|
||||
for_array(i, params->Tuple.variables) {
|
||||
Entity *v = params->Tuple.variables[i];
|
||||
if (i > 0) {
|
||||
type_writer_appendc(w, CANONICAL_PARAM_SEPARATOR);
|
||||
}
|
||||
type_writer_append(w, v->token.string.text, v->token.string.len);
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
|
||||
switch (v->kind) {
|
||||
case Entity_Variable:
|
||||
if (v->flags&EntityFlag_CVarArg) {
|
||||
type_writer_appendc(w, CANONICAL_PARAM_C_VARARG);
|
||||
}
|
||||
if (v->flags&EntityFlag_Ellipsis) {
|
||||
Type *slice = base_type(v->type);
|
||||
type_writer_appendc(w, CANONICAL_PARAM_VARARG);
|
||||
GB_ASSERT(v->type->kind == Type_Slice);
|
||||
write_type_to_canonical_string(w, slice->Slice.elem);
|
||||
} else {
|
||||
write_type_to_canonical_string(w, v->type);
|
||||
}
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
type_writer_appendc(w, CANONICAL_PARAM_TYPEID);
|
||||
write_type_to_canonical_string(w, v->type);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
{
|
||||
type_writer_appendc(w, CANONICAL_PARAM_CONST);
|
||||
gbString s = exact_value_to_string(v->Constant.value, 1<<16);
|
||||
type_writer_append(w, s, gb_string_length(s));
|
||||
gb_string_free(s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("TODO(bill): handle non type/const parapoly parameter values");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gb_internal u64 type_hash_canonical_type(Type *type) {
|
||||
if (type == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
u64 hash = fnv64a(nullptr, 0);
|
||||
TypeWriter w = {};
|
||||
type_writer_make_hasher(&w, &hash);
|
||||
write_type_to_canonical_string(&w, type);
|
||||
|
||||
return hash ? hash : 1;
|
||||
}
|
||||
|
||||
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
|
||||
TypeWriter w = {};
|
||||
type_writer_make_string(&w, allocator);
|
||||
write_type_to_canonical_string(&w, type);
|
||||
|
||||
gbString s = cast(gbString)w.user_data;
|
||||
return make_string(cast(u8 const *)s, gb_string_length(s));
|
||||
}
|
||||
|
||||
gb_internal gbString temp_canonical_string(Type *type) {
|
||||
TypeWriter w = {};
|
||||
type_writer_make_string(&w, temporary_allocator());
|
||||
write_type_to_canonical_string(&w, type);
|
||||
|
||||
return cast(gbString)w.user_data;
|
||||
}
|
||||
|
||||
gb_internal gbString string_canonical_entity_name(gbAllocator allocator, Entity *e) {
|
||||
TypeWriter w = {};
|
||||
type_writer_make_string(&w, allocator);
|
||||
write_canonical_entity_name(&w, e);
|
||||
return cast(gbString)w.user_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal void write_canonical_parent_prefix(TypeWriter *w, Entity *e) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->kind == Entity_Procedure || e->kind == Entity_TypeName) {
|
||||
if (e->kind == Entity_Procedure && (e->Procedure.is_export || e->Procedure.is_foreign)) {
|
||||
// no prefix
|
||||
return;
|
||||
}
|
||||
if (e->parent_proc_decl) {
|
||||
Entity *p = e->parent_proc_decl->entity;
|
||||
write_canonical_parent_prefix(w, p);
|
||||
type_writer_append(w, p->token.string.text, p->token.string.len);
|
||||
if (is_type_polymorphic(p->type)) {
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
write_type_to_canonical_string(w, p->type);
|
||||
}
|
||||
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
|
||||
|
||||
} else if (e->pkg && (scope_lookup_current(e->pkg->scope, e->token.string) == e)) {
|
||||
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
type_writer_appendc(w, "$");
|
||||
}
|
||||
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
|
||||
} else {
|
||||
String file_name = filename_without_directory(e->file->fullpath);
|
||||
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
type_writer_appendc(w, "$");
|
||||
}
|
||||
type_writer_append_fmt(w, CANONICAL_NAME_SEPARATOR "%.*s" CANONICAL_NAME_SEPARATOR, LIT(file_name));
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("TODO(bill): handle entity kind: %d", e->kind);
|
||||
}
|
||||
if (e->kind == Entity_Procedure && e->Procedure.is_anonymous) {
|
||||
String file_name = filename_without_directory(e->file->fullpath);
|
||||
type_writer_append_fmt(w, CANONICAL_ANON_PREFIX "_%.*s:%d", LIT(file_name), e->token.pos.offset);
|
||||
} else {
|
||||
type_writer_append(w, e->token.string.text, e->token.string.len);
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(e->type)) {
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
write_type_to_canonical_string(w, e->type);
|
||||
}
|
||||
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gb_internal void write_canonical_entity_name(TypeWriter *w, Entity *e) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
if (e->token.string == "_") {
|
||||
GB_PANIC("_ string");
|
||||
}
|
||||
if (e->token.string.len == 0) {
|
||||
GB_PANIC("empty string");
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
type_writer_append(w, e->Variable.link_name.text, e->Variable.link_name.len);
|
||||
return;
|
||||
} else if (is_foreign || is_export) {
|
||||
type_writer_append(w, e->token.string.text, e->token.string.len);
|
||||
return;
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
|
||||
type_writer_append(w, e->Procedure.link_name.text, e->Procedure.link_name.len);
|
||||
return;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
|
||||
type_writer_append(w, e->token.string.text, e->token.string.len);
|
||||
return;
|
||||
}
|
||||
|
||||
bool write_scope_index_suffix = false;
|
||||
|
||||
if (e->scope->flags & (ScopeFlag_Builtin)) {
|
||||
goto write_base_name;
|
||||
} else if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0 ||
|
||||
e->flags & EntityFlag_NotExported) {
|
||||
Scope *s = e->scope;
|
||||
|
||||
while ((s->flags & (ScopeFlag_Proc|ScopeFlag_File)) == 0 && s->decl_info == nullptr) {
|
||||
if (s->parent == nullptr) {
|
||||
break;
|
||||
}
|
||||
s = s->parent;
|
||||
}
|
||||
|
||||
if (s->decl_info != nullptr && s->decl_info->entity) {
|
||||
Entity *parent = s->decl_info->entity;
|
||||
write_canonical_parent_prefix(w, parent);
|
||||
if (e->scope->index > 0) {
|
||||
write_scope_index_suffix = true;
|
||||
}
|
||||
|
||||
goto write_base_name;
|
||||
} else if ((s->flags & ScopeFlag_File) && s->file != nullptr) {
|
||||
String file_name = filename_without_directory(s->file->fullpath);
|
||||
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
type_writer_appendc(w, "$");
|
||||
}
|
||||
type_writer_appendc(w, gb_bprintf(CANONICAL_NAME_SEPARATOR "[%.*s]" CANONICAL_NAME_SEPARATOR, LIT(file_name)));
|
||||
goto write_base_name;
|
||||
} else if (s->flags & (ScopeFlag_Builtin)) {
|
||||
goto write_base_name;
|
||||
}
|
||||
gb_printf_err("%s WEIRD ENTITY TYPE %s %u %p\n", token_pos_to_string(e->token.pos), type_to_string(e->type), s->flags, s->decl_info);
|
||||
|
||||
auto const print_scope_flags = [](Scope *s) {
|
||||
if (s->flags & ScopeFlag_Pkg) gb_printf_err("Pkg ");
|
||||
if (s->flags & ScopeFlag_Builtin) gb_printf_err("Builtin ");
|
||||
if (s->flags & ScopeFlag_Global) gb_printf_err("Global ");
|
||||
if (s->flags & ScopeFlag_File) gb_printf_err("File ");
|
||||
if (s->flags & ScopeFlag_Init) gb_printf_err("Init ");
|
||||
if (s->flags & ScopeFlag_Proc) gb_printf_err("Proc ");
|
||||
if (s->flags & ScopeFlag_Type) gb_printf_err("Type ");
|
||||
if (s->flags & ScopeFlag_HasBeenImported) gb_printf_err("HasBeenImported ");
|
||||
if (s->flags & ScopeFlag_ContextDefined) gb_printf_err("ContextDefined ");
|
||||
gb_printf_err("\n");
|
||||
};
|
||||
|
||||
print_scope_flags(s);
|
||||
GB_PANIC("weird entity %.*s", LIT(e->token.string));
|
||||
}
|
||||
if (e->pkg != nullptr) {
|
||||
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
|
||||
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
|
||||
}
|
||||
|
||||
write_base_name:
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_TypeName:
|
||||
{
|
||||
|
||||
Type *params = nullptr;
|
||||
Entity *parent = type_get_polymorphic_parent(e->type, ¶ms);
|
||||
if (parent && (parent->token.string == e->token.string)) {
|
||||
type_writer_append(w, parent->token.string.text, parent->token.string.len);
|
||||
write_canonical_params(w, params);
|
||||
} else {
|
||||
type_writer_append(w, e->token.string.text, e->token.string.len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Entity_Constant:
|
||||
// For debug symbols only
|
||||
/*fallthrough*/
|
||||
case Entity_Procedure:
|
||||
case Entity_Variable:
|
||||
type_writer_append(w, e->token.string.text, e->token.string.len);
|
||||
if (is_type_polymorphic(e->type)) {
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
write_type_to_canonical_string(w, e->type);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
GB_PANIC("TODO(bill): entity kind %d", e->kind);
|
||||
break;
|
||||
}
|
||||
|
||||
if (write_scope_index_suffix) {
|
||||
GB_ASSERT(e != nullptr && e->scope != nullptr);
|
||||
type_writer_append_fmt(w, CANONICAL_NAME_SEPARATOR "$%d", e->scope->index);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gb_internal bool is_in_doc_writer(void);
|
||||
|
||||
// NOTE(bill): This exists so that we deterministically hash a type by serializing it to a canonical string
|
||||
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
|
||||
if (type == nullptr) {
|
||||
type_writer_appendc(w, CANONICAL_NONE_TYPE); // none/void type
|
||||
return;
|
||||
}
|
||||
|
||||
type = default_type(type);
|
||||
GB_ASSERT(!is_type_untyped(type));
|
||||
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
type_writer_append(w, type->Basic.name.text, type->Basic.name.len);
|
||||
return;
|
||||
case Type_Pointer:
|
||||
type_writer_appendb(w, '^');
|
||||
write_type_to_canonical_string(w, type->Pointer.elem);
|
||||
return;
|
||||
case Type_MultiPointer:
|
||||
type_writer_appendc(w, "[^]");
|
||||
write_type_to_canonical_string(w, type->Pointer.elem);
|
||||
return;
|
||||
case Type_SoaPointer:
|
||||
type_writer_appendc(w, "#soa^");
|
||||
write_type_to_canonical_string(w, type->Pointer.elem);
|
||||
return;
|
||||
case Type_EnumeratedArray:
|
||||
if (type->EnumeratedArray.is_sparse) {
|
||||
type_writer_appendc(w, "#sparse");
|
||||
}
|
||||
type_writer_appendb(w, '[');
|
||||
write_type_to_canonical_string(w, type->EnumeratedArray.index);
|
||||
type_writer_appendb(w, ']');
|
||||
write_type_to_canonical_string(w, type->EnumeratedArray.elem);
|
||||
return;
|
||||
case Type_Array:
|
||||
type_writer_append_fmt(w, "[%lld]", cast(long long)type->Array.count);
|
||||
write_type_to_canonical_string(w, type->Array.elem);
|
||||
return;
|
||||
case Type_Slice:
|
||||
type_writer_appendc(w, "[]");
|
||||
write_type_to_canonical_string(w, type->Array.elem);
|
||||
return;
|
||||
case Type_DynamicArray:
|
||||
type_writer_appendc(w, "[dynamic]");
|
||||
write_type_to_canonical_string(w, type->DynamicArray.elem);
|
||||
return;
|
||||
case Type_SimdVector:
|
||||
type_writer_append_fmt(w, "#simd[%lld]", cast(long long)type->SimdVector.count);
|
||||
write_type_to_canonical_string(w, type->SimdVector.elem);
|
||||
return;
|
||||
case Type_Matrix:
|
||||
if (type->Matrix.is_row_major) {
|
||||
type_writer_appendc(w, "#row_major ");
|
||||
}
|
||||
type_writer_append_fmt(w, "matrix[%lld, %lld]", cast(long long)type->Matrix.row_count, cast(long long)type->Matrix.column_count);
|
||||
write_type_to_canonical_string(w, type->Matrix.elem);
|
||||
return;
|
||||
case Type_Map:
|
||||
type_writer_appendc(w, "map[");
|
||||
write_type_to_canonical_string(w, type->Map.key);
|
||||
type_writer_appendc(w, "]");
|
||||
write_type_to_canonical_string(w, type->Map.value);
|
||||
return;
|
||||
|
||||
case Type_Enum:
|
||||
type_writer_appendc(w, "enum");
|
||||
if (type->Enum.base_type != nullptr) {
|
||||
type_writer_appendb(w, ' ');
|
||||
write_type_to_canonical_string(w, type->Enum.base_type);
|
||||
type_writer_appendb(w, ' ');
|
||||
}
|
||||
type_writer_appendb(w, '{');
|
||||
for_array(i, type->Enum.fields) {
|
||||
Entity *f = type->Enum.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Constant);
|
||||
if (i > 0) {
|
||||
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
||||
}
|
||||
type_writer_append(w, f->token.string.text, f->token.string.len);
|
||||
type_writer_appendc(w, "=");
|
||||
|
||||
gbString s = exact_value_to_string(f->Constant.value, 1<<16);
|
||||
type_writer_append(w, s, gb_string_length(s));
|
||||
gb_string_free(s);
|
||||
}
|
||||
type_writer_appendb(w, '}');
|
||||
return;
|
||||
case Type_BitSet:
|
||||
type_writer_appendc(w, "bit_set[");
|
||||
if (type->BitSet.elem == nullptr) {
|
||||
type_writer_appendc(w, CANONICAL_NONE_TYPE);
|
||||
} else if (is_type_enum(type->BitSet.elem)) {
|
||||
write_type_to_canonical_string(w, type->BitSet.elem);
|
||||
} else {
|
||||
type_writer_append_fmt(w, "%lld", type->BitSet.lower);
|
||||
type_writer_append_fmt(w, CANONICAL_RANGE_OPERATOR);
|
||||
type_writer_append_fmt(w, "%lld", type->BitSet.upper);
|
||||
}
|
||||
if (type->BitSet.underlying != nullptr) {
|
||||
type_writer_appendc(w, ";");
|
||||
write_type_to_canonical_string(w, type->BitSet.underlying);
|
||||
}
|
||||
type_writer_appendc(w, "]");
|
||||
return;
|
||||
|
||||
case Type_Union:
|
||||
type_writer_appendc(w, "union");
|
||||
|
||||
switch (type->Union.kind) {
|
||||
case UnionType_no_nil: type_writer_appendc(w, "#no_nil"); break;
|
||||
case UnionType_shared_nil: type_writer_appendc(w, "#shared_nil"); break;
|
||||
}
|
||||
if (type->Union.custom_align != 0) {
|
||||
type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Union.custom_align);
|
||||
}
|
||||
type_writer_appendc(w, "{");
|
||||
for_array(i, type->Union.variants) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (i > 0) type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
||||
write_type_to_canonical_string(w, t);
|
||||
}
|
||||
type_writer_appendc(w, "}");
|
||||
return;
|
||||
case Type_Struct:
|
||||
if (type->Struct.soa_kind != StructSoa_None) {
|
||||
switch (type->Struct.soa_kind) {
|
||||
case StructSoa_Fixed: type_writer_append_fmt(w, "#soa[%lld]", cast(long long)type->Struct.soa_count); break;
|
||||
case StructSoa_Slice: type_writer_appendc(w, "#soa[]"); break;
|
||||
case StructSoa_Dynamic: type_writer_appendc(w, "#soa[dynamic]"); break;
|
||||
default: GB_PANIC("Unknown StructSoaKind"); break;
|
||||
}
|
||||
return write_type_to_canonical_string(w, type->Struct.soa_elem);
|
||||
}
|
||||
|
||||
type_writer_appendc(w, "struct");
|
||||
if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
|
||||
if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
|
||||
if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy");
|
||||
if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
|
||||
if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
|
||||
if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
|
||||
type_writer_appendb(w, '{');
|
||||
for_array(i, type->Struct.fields) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
if (i > 0) {
|
||||
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
||||
}
|
||||
type_writer_append(w, f->token.string.text, f->token.string.len);
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
write_type_to_canonical_string(w, f->type);
|
||||
String tag = {};
|
||||
if (type->Struct.tags != nullptr) {
|
||||
tag = type->Struct.tags[i];
|
||||
}
|
||||
if (tag.len != 0) {
|
||||
String s = quote_to_ascii(heap_allocator(), tag);
|
||||
type_writer_append(w, s.text, s.len);
|
||||
gb_free(heap_allocator(), s.text);
|
||||
}
|
||||
}
|
||||
type_writer_appendb(w, '}');
|
||||
return;
|
||||
|
||||
case Type_BitField:
|
||||
type_writer_appendc(w, "bit_field");
|
||||
write_type_to_canonical_string(w, type->BitField.backing_type);
|
||||
type_writer_appendc(w, " {");
|
||||
for (isize i = 0; i < type->BitField.fields.count; i++) {
|
||||
Entity *f = type->BitField.fields[i];
|
||||
if (i > 0) {
|
||||
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
||||
}
|
||||
type_writer_append(w, f->token.string.text, f->token.string.len);
|
||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||
write_type_to_canonical_string(w, f->type);
|
||||
type_writer_appendc(w, CANONICAL_BIT_FIELD_SEPARATOR);
|
||||
type_writer_append_fmt(w, "%u", type->BitField.bit_sizes[i]);
|
||||
}
|
||||
type_writer_appendc(w, " }");
|
||||
return;
|
||||
|
||||
case Type_Proc:
|
||||
type_writer_appendc(w, "proc");
|
||||
if (default_calling_convention() != type->Proc.calling_convention) {
|
||||
type_writer_appendc(w, "\"");
|
||||
type_writer_appendc(w, proc_calling_convention_strings[type->Proc.calling_convention]);
|
||||
type_writer_appendc(w, "\"");
|
||||
}
|
||||
|
||||
write_canonical_params(w, type->Proc.params);
|
||||
if (type->Proc.result_count > 0) {
|
||||
type_writer_appendc(w, "->");
|
||||
write_canonical_params(w, type->Proc.results);
|
||||
}
|
||||
return;
|
||||
|
||||
case Type_Generic:
|
||||
if (is_in_doc_writer()) {
|
||||
type_writer_appendc(w, "$");
|
||||
type_writer_append(w, type->Generic.name.text, type->Generic.name.len);
|
||||
type_writer_append_fmt(w, "%lld", cast(long long)type->Generic.id);
|
||||
} else {
|
||||
GB_PANIC("Type_Generic should never be hit");
|
||||
}
|
||||
return;
|
||||
|
||||
case Type_Named:
|
||||
if (type->Named.type_name != nullptr) {
|
||||
write_canonical_entity_name(w, type->Named.type_name);
|
||||
return;
|
||||
} else {
|
||||
type_writer_append(w, type->Named.name.text, type->Named.name.len);
|
||||
}
|
||||
return;
|
||||
|
||||
case Type_Tuple:
|
||||
type_writer_appendc(w, "params");
|
||||
write_canonical_params(w, type);
|
||||
return;
|
||||
default:
|
||||
GB_PANIC("unknown type kind %d %.*s", type->kind, LIT(type_strings[type->kind]));
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
General Rules for canonical name mangling
|
||||
|
||||
* No spaces between any values
|
||||
|
||||
* normal declarations - pkg::name
|
||||
* builtin names - just their normal name e.g. `i32` or `string`
|
||||
* nested (zero level) - pkg::parent1::parent2::name
|
||||
* nested (more scopes) - pkg::parent1::parent2::name[4]
|
||||
* [4] indicates the 4th scope within a procedure numbered in depth-first order
|
||||
* file private - pkg::[file_name]::name
|
||||
* Example: `pkg::[file.odin]::Type`
|
||||
* polymorphic procedure/type - pkg::foo:TYPE
|
||||
* naming convention for parameters
|
||||
* type
|
||||
* $typeid_based_name
|
||||
* $$constant_parameter
|
||||
* Example: `foo::to_thing:proc(u64)->([]u8)`
|
||||
* nested decl in polymorphic procedure - pkg::foo:TYPE::name
|
||||
* anonymous procedures - pkg::foo::$anon[file.odin:123]
|
||||
* 123 is the file offset in bytes
|
||||
*/
|
||||
|
||||
#define CANONICAL_TYPE_SEPARATOR ":"
|
||||
#define CANONICAL_NAME_SEPARATOR "::"
|
||||
// #define CANONICAL_NAME_SEPARATOR "·"
|
||||
|
||||
#define CANONICAL_BIT_FIELD_SEPARATOR "|"
|
||||
|
||||
#define CANONICAL_PARAM_SEPARATOR ","
|
||||
|
||||
#define CANONICAL_PARAM_TYPEID "$"
|
||||
#define CANONICAL_PARAM_CONST "$$"
|
||||
|
||||
#define CANONICAL_PARAM_C_VARARG "#c_vararg"
|
||||
#define CANONICAL_PARAM_VARARG ".."
|
||||
|
||||
#define CANONICAL_FIELD_SEPARATOR ","
|
||||
|
||||
#define CANONICAL_ANON_PREFIX "$anon"
|
||||
|
||||
#define CANONICAL_NONE_TYPE "<>"
|
||||
|
||||
#define CANONICAL_RANGE_OPERATOR "..="
|
||||
|
||||
struct TypeWriter;
|
||||
|
||||
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type);
|
||||
gb_internal void write_canonical_entity_name(TypeWriter *w, Entity *e);
|
||||
gb_internal u64 type_hash_canonical_type(Type *type);
|
||||
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type);
|
||||
gb_internal gbString temp_canonical_string(Type *type);
|
||||
|
||||
|
||||
gb_internal GB_COMPARE_PROC(type_info_pair_cmp);
|
||||
|
||||
|
||||
struct TypeInfoPair {
|
||||
Type *type;
|
||||
u64 hash; // see: type_hash_canonical_type
|
||||
};
|
||||
|
||||
struct TypeSet {
|
||||
TypeInfoPair *keys;
|
||||
usize count;
|
||||
usize capacity;
|
||||
};
|
||||
|
||||
static constexpr u64 TYPE_SET_TOMBSTONE = ~(u64)(0ull);
|
||||
|
||||
struct TypeSetIterator {
|
||||
TypeSet *set;
|
||||
usize index;
|
||||
|
||||
TypeSetIterator &operator++() noexcept {
|
||||
for (;;) {
|
||||
++index;
|
||||
if (set->capacity == index) {
|
||||
return *this;
|
||||
}
|
||||
TypeInfoPair key = set->keys[index];
|
||||
if (key.hash != 0 && key.hash != TYPE_SET_TOMBSTONE) {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(TypeSetIterator const &other) const noexcept {
|
||||
return this->set == other.set && this->index == other.index;
|
||||
}
|
||||
|
||||
|
||||
operator TypeInfoPair *() const {
|
||||
return &set->keys[index];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
gb_internal void type_set_init (TypeSet *s, isize capacity = 16);
|
||||
gb_internal void type_set_destroy(TypeSet *s);
|
||||
gb_internal Type *type_set_add (TypeSet *s, Type *ptr);
|
||||
gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair);
|
||||
gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed
|
||||
gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed
|
||||
gb_internal bool type_set_exists (TypeSet *s, Type *ptr);
|
||||
gb_internal void type_set_remove (TypeSet *s, Type *ptr);
|
||||
gb_internal void type_set_clear (TypeSet *s);
|
||||
gb_internal TypeInfoPair *type_set_retrieve(TypeSet *s, Type *ptr);
|
||||
|
||||
gb_internal TypeSetIterator begin(TypeSet &set) noexcept;
|
||||
gb_internal TypeSetIterator end(TypeSet &set) noexcept;
|
||||
|
||||
|
||||
template <typename V>
|
||||
gb_internal gb_inline V *map_get(PtrMap<u64, V> *h, Type *key) {
|
||||
return map_get(h, type_hash_canonical_type(key));
|
||||
}
|
||||
template <typename V>
|
||||
gb_internal gb_inline void map_set(PtrMap<u64, V> *h, Type *key, V const &value) {
|
||||
map_set(h, type_hash_canonical_type(key), value);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
gb_internal gb_inline V &map_must_get(PtrMap<u64, V> *h, Type *key) {
|
||||
V *ptr = map_get(h, type_hash_canonical_type(key));
|
||||
GB_ASSERT(ptr != nullptr);
|
||||
return *ptr;
|
||||
}
|
||||
+123
-113
@@ -3016,9 +3016,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
syntax_error(token, "Expected a type or range, got nothing");
|
||||
}
|
||||
|
||||
if (allow_token(f, Token_Semicolon)) {
|
||||
if (f->curr_token.kind == Token_Semicolon && f->curr_token.string == ";") {
|
||||
expect_token(f, Token_Semicolon);
|
||||
underlying = parse_type(f);
|
||||
} else if (allow_token(f, Token_Comma)) {
|
||||
} else if (allow_token(f, Token_Comma) || allow_token(f, Token_Semicolon)) {
|
||||
String p = token_to_string(f->prev_token);
|
||||
syntax_error(token_end_of_line(f, f->prev_token), "Expected a semicolon, got a %.*s", LIT(p));
|
||||
|
||||
@@ -4342,30 +4343,132 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
|
||||
}
|
||||
|
||||
|
||||
if (f->curr_token.kind == Token_Colon) {
|
||||
Array<Ast *> names = convert_to_ident_list(f, list, true, allow_poly_names); // Copy for semantic reasons
|
||||
if (f->curr_token.kind != Token_Colon) {
|
||||
// NOTE(bill): proc(Type, Type, Type)
|
||||
for (AstAndFlags const &item : list) {
|
||||
Ast *type = item.node;
|
||||
Token token = blank_token;
|
||||
if (allowed_flags&FieldFlag_Results) {
|
||||
// NOTE(bill): Make this nothing and not `_`
|
||||
token.string = str_lit("");
|
||||
}
|
||||
|
||||
auto names = array_make<Ast *>(ast_allocator(f), 1);
|
||||
token.pos = ast_token(type).pos;
|
||||
names[0] = ast_ident(f, token);
|
||||
u32 flags = check_field_prefixes(f, list.count, allowed_flags, item.flags);
|
||||
Token tag = {};
|
||||
Ast *param = ast_field(f, names, item.node, nullptr, flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
}
|
||||
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
return ast_field_list(f, start_token, params);
|
||||
}
|
||||
|
||||
// NOTE(bill): proc(ident, ident, ident: Type)
|
||||
|
||||
if (f->prev_token.kind == Token_Comma) {
|
||||
syntax_error(f->prev_token, "Trailing comma before a colon is not allowed");
|
||||
}
|
||||
Array<Ast *> names = convert_to_ident_list(f, list, true, allow_poly_names); // Copy for semantic reasons
|
||||
if (names.count == 0) {
|
||||
syntax_error(f->curr_token, "Empty field declaration");
|
||||
}
|
||||
bool any_polymorphic_names = check_procedure_name_list(names);
|
||||
u32 set_flags = 0;
|
||||
if (list.count > 0) {
|
||||
set_flags = list[0].flags;
|
||||
}
|
||||
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
|
||||
total_name_count += names.count;
|
||||
|
||||
Ast *type = nullptr;
|
||||
Ast *default_value = nullptr;
|
||||
Token tag = {};
|
||||
|
||||
expect_token_after(f, Token_Colon, "field list");
|
||||
if (f->curr_token.kind != Token_Eq) {
|
||||
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
|
||||
Ast *tt = unparen_expr(type);
|
||||
if (tt == nullptr) {
|
||||
syntax_error(f->prev_token, "Invalid type expression in field list");
|
||||
} else if (is_signature && !any_polymorphic_names && tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
|
||||
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_token(f, Token_Eq)) {
|
||||
default_value = parse_expr(f, false);
|
||||
if (!allow_default_parameters) {
|
||||
syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
|
||||
default_value = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (default_value != nullptr && names.count > 1) {
|
||||
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
|
||||
}
|
||||
|
||||
if (allowed_flags == FieldFlag_Struct && default_value != nullptr) {
|
||||
syntax_error(default_value, "Default parameters are not allowed for structs");
|
||||
default_value = nullptr;
|
||||
}
|
||||
|
||||
if (type != nullptr && type->kind == Ast_Ellipsis) {
|
||||
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
|
||||
seen_ellipsis = true;
|
||||
if (names.count != 1) {
|
||||
syntax_error(type, "Variadic parameters can only have one field name");
|
||||
}
|
||||
} else if (seen_ellipsis && default_value == nullptr) {
|
||||
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
|
||||
}
|
||||
|
||||
if (type != nullptr && default_value == nullptr) {
|
||||
if (f->curr_token.kind == Token_String) {
|
||||
tag = expect_token(f, Token_String);
|
||||
if ((allowed_flags & FieldFlag_Tags) == 0) {
|
||||
syntax_error(tag, "Field tags are only allowed within structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool more_fields = allow_field_separator(f);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (!more_fields) {
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
return ast_field_list(f, start_token, params);
|
||||
}
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF &&
|
||||
f->curr_token.kind != Token_Semicolon) {
|
||||
CommentGroup *docs = f->lead_comment;
|
||||
|
||||
if (!is_signature) parse_enforce_tabs(f);
|
||||
u32 set_flags = parse_field_prefixes(f);
|
||||
Token tag = {};
|
||||
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
|
||||
if (names.count == 0) {
|
||||
syntax_error(f->curr_token, "Empty field declaration");
|
||||
break;
|
||||
}
|
||||
bool any_polymorphic_names = check_procedure_name_list(names);
|
||||
u32 set_flags = 0;
|
||||
if (list.count > 0) {
|
||||
set_flags = list[0].flags;
|
||||
}
|
||||
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
|
||||
total_name_count += names.count;
|
||||
|
||||
Ast *type = nullptr;
|
||||
Ast *default_value = nullptr;
|
||||
Token tag = {};
|
||||
|
||||
expect_token_after(f, Token_Colon, "field list");
|
||||
if (f->curr_token.kind != Token_Eq) {
|
||||
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
|
||||
Ast *tt = unparen_expr(type);
|
||||
if (tt == nullptr) {
|
||||
syntax_error(f->prev_token, "Invalid type expression in field list");
|
||||
} else if (is_signature && !any_polymorphic_names && tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
|
||||
if (is_signature && !any_polymorphic_names &&
|
||||
tt != nullptr &&
|
||||
tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
|
||||
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
|
||||
}
|
||||
}
|
||||
@@ -4382,11 +4485,6 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
|
||||
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
|
||||
}
|
||||
|
||||
if (allowed_flags == FieldFlag_Struct && default_value != nullptr) {
|
||||
syntax_error(default_value, "Default parameters are not allowed for structs");
|
||||
default_value = nullptr;
|
||||
}
|
||||
|
||||
if (type != nullptr && type->kind == Ast_Ellipsis) {
|
||||
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
|
||||
seen_ellipsis = true;
|
||||
@@ -4406,105 +4504,14 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
|
||||
}
|
||||
}
|
||||
|
||||
bool more_fields = allow_field_separator(f);
|
||||
|
||||
bool ok = allow_field_separator(f);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (!more_fields) {
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
return ast_field_list(f, start_token, params);
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF &&
|
||||
f->curr_token.kind != Token_Semicolon) {
|
||||
CommentGroup *docs = f->lead_comment;
|
||||
|
||||
if (!is_signature) parse_enforce_tabs(f);
|
||||
u32 set_flags = parse_field_prefixes(f);
|
||||
Token tag = {};
|
||||
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
|
||||
if (names.count == 0) {
|
||||
syntax_error(f->curr_token, "Empty field declaration");
|
||||
break;
|
||||
}
|
||||
bool any_polymorphic_names = check_procedure_name_list(names);
|
||||
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
|
||||
total_name_count += names.count;
|
||||
|
||||
Ast *type = nullptr;
|
||||
Ast *default_value = nullptr;
|
||||
expect_token_after(f, Token_Colon, "field list");
|
||||
if (f->curr_token.kind != Token_Eq) {
|
||||
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
|
||||
Ast *tt = unparen_expr(type);
|
||||
if (is_signature && !any_polymorphic_names &&
|
||||
tt != nullptr &&
|
||||
tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
|
||||
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_token(f, Token_Eq)) {
|
||||
default_value = parse_expr(f, false);
|
||||
if (!allow_default_parameters) {
|
||||
syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
|
||||
default_value = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (default_value != nullptr && names.count > 1) {
|
||||
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
|
||||
}
|
||||
|
||||
if (type != nullptr && type->kind == Ast_Ellipsis) {
|
||||
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
|
||||
seen_ellipsis = true;
|
||||
if (names.count != 1) {
|
||||
syntax_error(type, "Variadic parameters can only have one field name");
|
||||
}
|
||||
} else if (seen_ellipsis && default_value == nullptr) {
|
||||
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
|
||||
}
|
||||
|
||||
if (type != nullptr && default_value == nullptr) {
|
||||
if (f->curr_token.kind == Token_String) {
|
||||
tag = expect_token(f, Token_String);
|
||||
if ((allowed_flags & FieldFlag_Tags) == 0) {
|
||||
syntax_error(tag, "Field tags are only allowed within structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ok = allow_field_separator(f);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
return ast_field_list(f, start_token, params);
|
||||
}
|
||||
|
||||
for (AstAndFlags const &item : list) {
|
||||
Ast *type = item.node;
|
||||
Token token = blank_token;
|
||||
if (allowed_flags&FieldFlag_Results) {
|
||||
// NOTE(bill): Make this nothing and not `_`
|
||||
token.string = str_lit("");
|
||||
}
|
||||
|
||||
auto names = array_make<Ast *>(ast_allocator(f), 1);
|
||||
token.pos = ast_token(type).pos;
|
||||
names[0] = ast_ident(f, token);
|
||||
u32 flags = check_field_prefixes(f, list.count, allowed_flags, item.flags);
|
||||
Token tag = {};
|
||||
Ast *param = ast_field(f, names, item.node, nullptr, flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
}
|
||||
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
@@ -4572,6 +4579,9 @@ gb_internal Ast *parse_do_body(AstFile *f, Token const &token, char const *msg)
|
||||
gb_internal bool parse_control_statement_semicolon_separator(AstFile *f) {
|
||||
Token tok = peek_token(f);
|
||||
if (tok.kind != Token_OpenBrace) {
|
||||
if (f->curr_token.kind == Token_Semicolon && f->curr_token.string != ";") {
|
||||
syntax_error(token_end_of_line(f, f->prev_token), "Expected ';', got newline");
|
||||
}
|
||||
return allow_token(f, Token_Semicolon);
|
||||
}
|
||||
if (f->curr_token.string == ";") {
|
||||
|
||||
+5
-5
@@ -42,7 +42,7 @@ gb_internal void ptr_set_destroy(PtrSet<T> *s) {
|
||||
|
||||
template <typename T>
|
||||
gb_internal isize ptr_set__find(PtrSet<T> *s, T ptr) {
|
||||
GB_ASSERT(ptr != nullptr);
|
||||
GB_ASSERT(ptr != 0);
|
||||
if (s->count != 0) {
|
||||
#if 0
|
||||
for (usize i = 0; i < s->capacity; i++) {
|
||||
@@ -58,7 +58,7 @@ gb_internal isize ptr_set__find(PtrSet<T> *s, T ptr) {
|
||||
T key = s->keys[hash_index];
|
||||
if (key == ptr) {
|
||||
return hash_index;
|
||||
} else if (key == nullptr) {
|
||||
} else if (key == 0) {
|
||||
return -1;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
@@ -122,7 +122,7 @@ gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it pre
|
||||
for (usize i = 0; i < s->capacity; i++) {
|
||||
T *key = &s->keys[hash_index];
|
||||
GB_ASSERT(*key != ptr);
|
||||
if (*key == (T)PtrSet<T>::TOMBSTONE || *key == nullptr) {
|
||||
if (*key == (T)PtrSet<T>::TOMBSTONE || *key == 0) {
|
||||
*key = ptr;
|
||||
s->count++;
|
||||
return false;
|
||||
@@ -169,7 +169,7 @@ struct PtrSetIterator {
|
||||
return *this;
|
||||
}
|
||||
T key = set->keys[index];
|
||||
if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
|
||||
if (key != 0 && key != (T)PtrSet<T>::TOMBSTONE) {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
|
||||
usize index = 0;
|
||||
while (index < set.capacity) {
|
||||
T key = set.keys[index];
|
||||
if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
|
||||
if (key != 0 && key != (T)PtrSet<T>::TOMBSTONE) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
|
||||
+92
-77
@@ -1,5 +1,5 @@
|
||||
struct Scope;
|
||||
struct Ast;
|
||||
struct Scope;
|
||||
struct Entity;
|
||||
|
||||
enum BasicKind {
|
||||
@@ -161,10 +161,10 @@ struct TypeStruct {
|
||||
|
||||
struct TypeUnion {
|
||||
Slice<Type *> variants;
|
||||
|
||||
|
||||
Ast * node;
|
||||
Scope * scope;
|
||||
|
||||
|
||||
i64 variant_block_size;
|
||||
i64 custom_align;
|
||||
Type * polymorphic_params; // Type_Tuple
|
||||
@@ -503,9 +503,9 @@ gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
|
||||
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
|
||||
{Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}},
|
||||
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
|
||||
{Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}},
|
||||
|
||||
{Type_Basic, {Basic_typeid, 0, -1, STR_LIT("typeid")}},
|
||||
{Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}},
|
||||
|
||||
// Endian
|
||||
{Type_Basic, {Basic_i16le, BasicFlag_Integer | BasicFlag_EndianLittle, 2, STR_LIT("i16le")}},
|
||||
@@ -856,40 +856,6 @@ gb_internal void type_path_pop(TypePath *tp) {
|
||||
#define FAILURE_SIZE 0
|
||||
#define FAILURE_ALIGNMENT 0
|
||||
|
||||
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
|
||||
|
||||
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
|
||||
if (t == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (type_ptr_set_exists(s, t)) {
|
||||
return true;
|
||||
}
|
||||
ptr_set_add(s, t);
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
|
||||
if (t == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ptr_set_exists(s, t)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(bill, 2019-10-05): This is very slow and it's probably a lot
|
||||
// faster to cache types correctly
|
||||
for (Type *f : *s) {
|
||||
if (are_types_identical(t, f)) {
|
||||
ptr_set_add(s, t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal Type *base_type(Type *t) {
|
||||
for (;;) {
|
||||
if (t == nullptr) {
|
||||
@@ -1438,7 +1404,7 @@ gb_internal bool is_type_matrix(Type *t) {
|
||||
gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
|
||||
t = base_type(t);
|
||||
GB_ASSERT(t->kind == Type_Matrix);
|
||||
|
||||
|
||||
Type *elem = t->Matrix.elem;
|
||||
i64 row_count = gb_max(t->Matrix.row_count, 1);
|
||||
i64 column_count = gb_max(t->Matrix.column_count, 1);
|
||||
@@ -1450,15 +1416,15 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
|
||||
|
||||
i64 elem_align = type_align_of_internal(elem, tp);
|
||||
if (pop) type_path_pop(tp);
|
||||
|
||||
|
||||
i64 elem_size = type_size_of(elem);
|
||||
|
||||
|
||||
|
||||
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
|
||||
// It would be better for performance to pad each column so that each column
|
||||
// could be maximally aligned but as a compromise, having no padding will be
|
||||
// beneficial to third libraries that assume no padding
|
||||
|
||||
|
||||
i64 total_expected_size = row_count*column_count*elem_size;
|
||||
// i64 min_alignment = prev_pow2(elem_align * row_count);
|
||||
i64 min_alignment = prev_pow2(total_expected_size);
|
||||
@@ -1466,7 +1432,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
|
||||
min_alignment >>= 1;
|
||||
}
|
||||
min_alignment = gb_max(min_alignment, elem_align);
|
||||
|
||||
|
||||
i64 align = gb_min(min_alignment, build_context.max_simd_align);
|
||||
return align;
|
||||
}
|
||||
@@ -1480,7 +1446,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
|
||||
} else if (t->Matrix.row_count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
i64 elem_size;
|
||||
if (tp != nullptr) {
|
||||
elem_size = type_size_of_internal(t->Matrix.elem, tp);
|
||||
@@ -1489,7 +1455,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
|
||||
}
|
||||
|
||||
i64 stride_in_bytes = 0;
|
||||
|
||||
|
||||
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
|
||||
// It would be better for performance to pad each column/row so that each column/row
|
||||
// could be maximally aligned but as a compromise, having no padding will be
|
||||
@@ -1545,7 +1511,7 @@ gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
|
||||
gb_internal i64 matrix_column_major_index_to_offset(Type *t, i64 index) {
|
||||
t = base_type(t);
|
||||
GB_ASSERT(t->kind == Type_Matrix);
|
||||
|
||||
|
||||
i64 row_index = index%t->Matrix.row_count;
|
||||
i64 column_index = index/t->Matrix.row_count;
|
||||
return matrix_indices_to_offset(t, row_index, column_index);
|
||||
@@ -1566,7 +1532,7 @@ gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
|
||||
return true;
|
||||
} else if (is_type_complex(t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (t->kind == Type_Generic) {
|
||||
return true;
|
||||
}
|
||||
@@ -2119,6 +2085,26 @@ gb_internal bool is_type_sliceable(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal Entity *type_get_polymorphic_parent(Type *t, Type **params_) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
Type *parent = nullptr;
|
||||
if (t->kind == Type_Struct) {
|
||||
parent = t->Struct.polymorphic_parent;
|
||||
if (params_) *params_ = t->Struct.polymorphic_params;
|
||||
} else if (t->kind == Type_Union) {
|
||||
parent = t->Union.polymorphic_parent;
|
||||
if (params_) *params_ = t->Union.polymorphic_params;
|
||||
}
|
||||
if (parent != nullptr) {
|
||||
GB_ASSERT(parent->kind == Type_Named);
|
||||
|
||||
return parent->Named.type_name;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_polymorphic_record(Type *t) {
|
||||
t = base_type(t);
|
||||
@@ -2485,7 +2471,7 @@ gb_internal bool is_type_simple_compare(Type *t) {
|
||||
case Type_Proc:
|
||||
case Type_BitSet:
|
||||
return true;
|
||||
|
||||
|
||||
case Type_Matrix:
|
||||
return is_type_simple_compare(t->Matrix.elem);
|
||||
|
||||
@@ -2732,7 +2718,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
|
||||
|
||||
case Type_Array:
|
||||
return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem);
|
||||
|
||||
|
||||
case Type_Matrix:
|
||||
return x->Matrix.row_count == y->Matrix.row_count &&
|
||||
x->Matrix.column_count == y->Matrix.column_count &&
|
||||
@@ -2757,7 +2743,37 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
|
||||
|
||||
|
||||
case Type_Enum:
|
||||
return x == y; // NOTE(bill): All enums are unique
|
||||
if (x == y) {
|
||||
return true;
|
||||
}
|
||||
if (x->Enum.fields.count != y->Enum.fields.count) {
|
||||
return false;
|
||||
}
|
||||
if (!are_types_identical(x->Enum.base_type, y->Enum.base_type)) {
|
||||
return false;
|
||||
}
|
||||
if (x->Enum.min_value_index != y->Enum.min_value_index) {
|
||||
return false;
|
||||
}
|
||||
if (x->Enum.max_value_index != y->Enum.max_value_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < x->Enum.fields.count; i++) {
|
||||
Entity *a = x->Enum.fields[i];
|
||||
Entity *b = y->Enum.fields[i];
|
||||
if (a->token.string != b->token.string) {
|
||||
return false;
|
||||
}
|
||||
GB_ASSERT(a->kind == b->kind);
|
||||
GB_ASSERT(a->kind == Entity_Constant);
|
||||
bool same = compare_exact_values(Token_CmpEq, a->Constant.value, b->Constant.value);
|
||||
if (!same) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case Type_Union:
|
||||
if (x->Union.variants.count == y->Union.variants.count &&
|
||||
@@ -2815,7 +2831,9 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return are_types_identical(x->Struct.polymorphic_params, y->Struct.polymorphic_params);
|
||||
// TODO(bill): Which is the correct logic here?
|
||||
// return are_types_identical(x->Struct.polymorphic_params, y->Struct.polymorphic_params);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3592,7 +3610,7 @@ gb_internal bool are_struct_fields_reordered(Type *type) {
|
||||
return false;
|
||||
}
|
||||
GB_ASSERT(type->Struct.offsets != nullptr);
|
||||
|
||||
|
||||
i64 prev_offset = 0;
|
||||
for_array(i, type->Struct.fields) {
|
||||
i64 offset = type->Struct.offsets[i];
|
||||
@@ -3613,9 +3631,9 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
|
||||
return {};
|
||||
}
|
||||
GB_ASSERT(type->Struct.offsets != nullptr);
|
||||
|
||||
|
||||
auto indices = slice_make<i32>(allocator, type->Struct.fields.count);
|
||||
|
||||
|
||||
i64 prev_offset = 0;
|
||||
bool is_ordered = true;
|
||||
for_array(i, indices) {
|
||||
@@ -3630,14 +3648,14 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
|
||||
isize n = indices.count;
|
||||
for (isize i = 1; i < n; i++) {
|
||||
isize j = i;
|
||||
|
||||
|
||||
while (j > 0 && type->Struct.offsets[indices[j-1]] > type->Struct.offsets[indices[j]]) {
|
||||
gb_swap(i32, indices[j-1], indices[j]);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
@@ -3685,8 +3703,8 @@ gb_internal i64 type_size_of(Type *t) {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string: size = 2*build_context.int_size; break;
|
||||
case Basic_cstring: size = build_context.ptr_size; break;
|
||||
case Basic_any: size = 2*build_context.ptr_size; break;
|
||||
case Basic_typeid: size = build_context.ptr_size; break;
|
||||
case Basic_any: size = 16; break;
|
||||
case Basic_typeid: size = 8; break;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
size = build_context.int_size;
|
||||
@@ -3748,8 +3766,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string: return build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_any: return build_context.ptr_size;
|
||||
case Basic_typeid: return build_context.ptr_size;
|
||||
case Basic_any: return 8;
|
||||
case Basic_typeid: return 8;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
return build_context.int_size;
|
||||
@@ -3887,8 +3905,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
|
||||
// IMPORTANT TODO(bill): Figure out the alignment of vector types
|
||||
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2);
|
||||
}
|
||||
|
||||
case Type_Matrix:
|
||||
|
||||
case Type_Matrix:
|
||||
return matrix_align_of(t, path);
|
||||
|
||||
case Type_SoaPointer:
|
||||
@@ -3999,8 +4017,8 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
|
||||
switch (kind) {
|
||||
case Basic_string: return 2*build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_any: return 2*build_context.ptr_size;
|
||||
case Basic_typeid: return build_context.ptr_size;
|
||||
case Basic_any: return 16;
|
||||
case Basic_typeid: return 8;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
return build_context.int_size;
|
||||
@@ -4175,7 +4193,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
|
||||
Type *elem = t->SimdVector.elem;
|
||||
return count * type_size_of_internal(elem, path);
|
||||
}
|
||||
|
||||
|
||||
case Type_Matrix: {
|
||||
i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path);
|
||||
if (t->Matrix.is_row_major) {
|
||||
@@ -4236,7 +4254,7 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
|
||||
return 0; // data
|
||||
case 1:
|
||||
if (field_type_) *field_type_ = t_typeid;
|
||||
return build_context.ptr_size; // id
|
||||
return 8; // id
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4307,8 +4325,8 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_any) {
|
||||
switch (index) {
|
||||
case 0: t = t_type_info_ptr; break;
|
||||
case 1: t = t_rawptr; break;
|
||||
case 0: t = t_rawptr; break;
|
||||
case 1: t = t_typeid; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4580,7 +4598,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
|
||||
break;
|
||||
|
||||
case Type_Array:
|
||||
str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
|
||||
str = gb_string_appendc(str, gb_bprintf("[%lld]", cast(long long)type->Array.count));
|
||||
str = write_type_to_string(str, type->Array.elem);
|
||||
break;
|
||||
|
||||
@@ -4753,10 +4771,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
|
||||
}
|
||||
break;
|
||||
case ProcCC_CDecl:
|
||||
str = gb_string_appendc(str, " \"cdecl\" ");
|
||||
str = gb_string_appendc(str, " \"c\" ");
|
||||
break;
|
||||
case ProcCC_StdCall:
|
||||
str = gb_string_appendc(str, " \"stdcall\" ");
|
||||
str = gb_string_appendc(str, " \"std\" ");
|
||||
break;
|
||||
case ProcCC_FastCall:
|
||||
str = gb_string_appendc(str, " \"fastcall\" ");
|
||||
@@ -4814,7 +4832,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
|
||||
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
|
||||
str = write_type_to_string(str, type->SimdVector.elem);
|
||||
break;
|
||||
|
||||
|
||||
case Type_Matrix:
|
||||
if (type->Matrix.is_row_major) {
|
||||
str = gb_string_appendc(str, "#row_major ");
|
||||
@@ -4855,6 +4873,3 @@ gb_internal gbString type_to_string(Type *type, bool shorthand) {
|
||||
gb_internal gbString type_to_string_shorthand(Type *type) {
|
||||
return type_to_string(type, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user