mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
+13
-15
@@ -418,6 +418,7 @@ enum LinkerChoice : i32 {
|
||||
Linker_Default = 0,
|
||||
Linker_lld,
|
||||
Linker_radlink,
|
||||
Linker_mold,
|
||||
|
||||
Linker_COUNT,
|
||||
};
|
||||
@@ -433,6 +434,7 @@ String linker_choices[Linker_COUNT] = {
|
||||
str_lit("default"),
|
||||
str_lit("lld"),
|
||||
str_lit("radlink"),
|
||||
str_lit("mold"),
|
||||
};
|
||||
|
||||
enum IntegerDivisionByZeroKind : u8 {
|
||||
@@ -546,6 +548,8 @@ struct BuildContext {
|
||||
bool ignore_microsoft_magic;
|
||||
bool linker_map_file;
|
||||
|
||||
bool build_diagnostics;
|
||||
|
||||
bool use_single_module;
|
||||
bool use_separate_modules;
|
||||
bool module_per_file;
|
||||
@@ -554,6 +558,8 @@ struct BuildContext {
|
||||
|
||||
bool internal_no_inline;
|
||||
bool internal_by_value;
|
||||
bool internal_weak_monomorphization;
|
||||
bool internal_ignore_llvm_verification;
|
||||
|
||||
bool no_threaded_checker;
|
||||
|
||||
@@ -964,14 +970,6 @@ gb_internal bool is_excluded_target_filename(String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (build_context.command_kind != Command_test) {
|
||||
String test_suffix = str_lit("_test");
|
||||
if (string_ends_with(name, test_suffix) && name != test_suffix) {
|
||||
// Ignore *_test.odin files
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
String str1 = {};
|
||||
String str2 = {};
|
||||
isize n = 0;
|
||||
@@ -1126,7 +1124,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
|
||||
text = permanent_alloc_array<wchar_t>(len+1);
|
||||
|
||||
GetModuleFileNameW(nullptr, text, cast(int)len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len));
|
||||
@@ -1163,8 +1161,8 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
auto path_buf = array_make<char>(temporary_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
@@ -1181,7 +1179,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
|
||||
text = permanent_alloc_array<u8>(len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
|
||||
@@ -1233,7 +1231,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
|
||||
text = permanent_alloc_array<u8>(len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
|
||||
@@ -1394,7 +1392,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
|
||||
text = permanent_alloc_array<u8>(len + 1);
|
||||
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
@@ -1429,7 +1427,7 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
|
||||
|
||||
len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
|
||||
wchar_t *text = permanent_alloc_array<wchar_t>(len+1);
|
||||
GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr);
|
||||
mutex_unlock(&fullpath_mutex);
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ i32 bundle_android(String original_init_directory) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count);
|
||||
int *dir_numbers = temporary_alloc_array<int>(possible_valid_dirs.count);
|
||||
|
||||
char buf[1024] = {};
|
||||
for_array(i, possible_valid_dirs) {
|
||||
|
||||
+13
-13
@@ -354,7 +354,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
}
|
||||
|
||||
isize const arg_offset = 1;
|
||||
auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
|
||||
auto param_types = permanent_slice_make<Type *>(ce->args.count-arg_offset);
|
||||
param_types[0] = t_objc_id;
|
||||
param_types[1] = sel_type;
|
||||
|
||||
@@ -462,7 +462,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
{
|
||||
// NOTE(harold): The last argument specified in the call is the handler proc,
|
||||
// any other arguments before it are capture by-copy arguments.
|
||||
auto param_operands = slice_make<Operand>(permanent_allocator(), ce->args.count);
|
||||
auto param_operands = permanent_slice_make<Operand>(ce->args.count);
|
||||
|
||||
isize capture_arg_count = ce->args.count - 1;
|
||||
|
||||
@@ -3555,7 +3555,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
|
||||
case BuiltinProc_compress_values: {
|
||||
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
|
||||
Operand *ops = temporary_alloc_array<Operand>(ce->args.count);
|
||||
|
||||
isize value_count = 0;
|
||||
|
||||
@@ -3686,9 +3686,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
operand->mode = Addressing_Value;
|
||||
} else {
|
||||
Type *st = alloc_type_struct_complete();
|
||||
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
|
||||
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
|
||||
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
|
||||
st->Struct.fields = permanent_slice_make<Entity *>(value_count);
|
||||
st->Struct.tags = permanent_alloc_array<String>(value_count);
|
||||
st->Struct.offsets = permanent_alloc_array<i64>(value_count);
|
||||
|
||||
Scope *scope = create_scope(c->info, nullptr);
|
||||
|
||||
@@ -4388,7 +4388,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
elem = alloc_type_struct();
|
||||
elem->Struct.scope = s;
|
||||
elem->Struct.fields = slice_from_array(fields);
|
||||
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
|
||||
elem->Struct.tags = permanent_alloc_array<String>(fields.count);
|
||||
elem->Struct.node = dummy_node_struct;
|
||||
type_set_offsets(elem);
|
||||
wait_signal_set(&elem->Struct.fields_wait_signal);
|
||||
@@ -4420,7 +4420,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1);
|
||||
auto types = permanent_slice_make<Type *>(t->Struct.fields.count-1);
|
||||
for_array(i, types) {
|
||||
Entity *f = t->Struct.fields[i];
|
||||
GB_ASSERT(f->type->kind == Type_MultiPointer);
|
||||
@@ -4756,8 +4756,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
if (is_type_array(elem)) {
|
||||
Type *old_array = base_type(elem);
|
||||
soa_struct = alloc_type_struct();
|
||||
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count);
|
||||
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count);
|
||||
soa_struct->Struct.fields = permanent_slice_make<Entity *>(cast(isize)old_array->Array.count);
|
||||
soa_struct->Struct.tags = permanent_alloc_array<String>(cast(isize)old_array->Array.count);
|
||||
soa_struct->Struct.node = operand->expr;
|
||||
soa_struct->Struct.soa_kind = StructSoa_Fixed;
|
||||
soa_struct->Struct.soa_elem = elem;
|
||||
@@ -4789,8 +4789,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
|
||||
Type *old_struct = base_type(elem);
|
||||
soa_struct = alloc_type_struct();
|
||||
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
|
||||
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count);
|
||||
soa_struct->Struct.fields = permanent_slice_make<Entity *>(old_struct->Struct.fields.count);
|
||||
soa_struct->Struct.tags = permanent_alloc_array<String>(old_struct->Struct.fields.count);
|
||||
soa_struct->Struct.node = operand->expr;
|
||||
soa_struct->Struct.soa_kind = StructSoa_Fixed;
|
||||
soa_struct->Struct.soa_elem = elem;
|
||||
@@ -6182,7 +6182,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
|
||||
Type *new_type = alloc_type_union();
|
||||
auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
|
||||
auto variants = permanent_slice_make<Type *>(bt->Union.variants.count);
|
||||
for_array(i, bt->Union.variants) {
|
||||
variants[i] = alloc_type_pointer(bt->Union.variants[i]);
|
||||
}
|
||||
|
||||
+76
-11
@@ -162,8 +162,6 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
|
||||
if (found_scope == nullptr) {
|
||||
return;
|
||||
}
|
||||
rw_mutex_lock(&found_scope->mutex);
|
||||
defer (rw_mutex_unlock(&found_scope->mutex));
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
||||
// original entity was still used check checked, but the checking was only
|
||||
@@ -172,7 +170,9 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
|
||||
// Therefore two things can be done: the type can be assigned to state that it
|
||||
// has been "evaluated" and the variant data can be copied across
|
||||
|
||||
rw_mutex_lock(&found_scope->mutex);
|
||||
string_map_set(&found_scope->elements, original_name, new_entity);
|
||||
rw_mutex_unlock(&found_scope->mutex);
|
||||
|
||||
original_entity->flags |= EntityFlag_Overridden;
|
||||
original_entity->type = new_entity->type;
|
||||
@@ -851,6 +851,50 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
|
||||
}
|
||||
}
|
||||
|
||||
Type *x_base = base_type(x);
|
||||
Type *y_base = base_type(y);
|
||||
|
||||
if (x_base == y_base) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x_base->kind == y_base->kind &&
|
||||
x_base->kind == Type_Struct) {
|
||||
i64 xs = type_size_of(x_base);
|
||||
i64 ys = type_size_of(y_base);
|
||||
|
||||
i64 xa = type_align_of(x_base);
|
||||
i64 ya = type_align_of(y_base);
|
||||
|
||||
|
||||
if (x_base->Struct.is_raw_union == y_base->Struct.is_raw_union &&
|
||||
xs == ys && xa == ya) {
|
||||
if (xs > 16) {
|
||||
// @@ABI NOTE(bill): Just allow anything over 16-bytes to be allowed, because on all current ABIs
|
||||
// it will be passed by point
|
||||
// NOTE(bill): this must be changed when ABI changes
|
||||
return true;
|
||||
}
|
||||
if (x_base->Struct.is_raw_union) {
|
||||
return true;
|
||||
}
|
||||
if (x->Struct.fields.count == y->Struct.fields.count) {
|
||||
for (isize i = 0; i < x->Struct.fields.count; i++) {
|
||||
Entity *a = x->Struct.fields[i];
|
||||
Entity *b = y->Struct.fields[i];
|
||||
bool similar = signature_parameter_similar_enough(a->type, b->type);
|
||||
if (!similar) {
|
||||
// NOTE(bill): If the fields are not similar enough, then stop.
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
// HACK NOTE(bill): Allow this for the time begin until it actually becomes a practical problem
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
end:;
|
||||
return are_types_identical(x, y);
|
||||
}
|
||||
|
||||
@@ -948,7 +992,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
|
||||
error(ident, "foreign library names must be an identifier");
|
||||
} else {
|
||||
String name = ident->Ident.token.string;
|
||||
Entity *found = scope_lookup(ctx->scope, name);
|
||||
Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash);
|
||||
|
||||
if (found == nullptr) {
|
||||
if (is_blank_ident(name)) {
|
||||
@@ -1549,7 +1593,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
"\tother at %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
} else if (name == "main") {
|
||||
if (d->entity->pkg->kind != Package_Runtime) {
|
||||
if (d->entity.load()->pkg->kind != Package_Runtime) {
|
||||
error(d->proc_lit, "The link name 'main' is reserved for internal use");
|
||||
}
|
||||
} else {
|
||||
@@ -1565,7 +1609,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
|
||||
gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
@@ -1686,7 +1730,28 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
|
||||
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
|
||||
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
|
||||
if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
|
||||
ERROR_BLOCK();
|
||||
error(o.expr, "Variables declared with @(rodata) must have constant initialization");
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
if (is_type_struct(e->type) && expr && expr->kind == Ast_CompoundLit) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
for (Ast *elem_ : cl->elems) {
|
||||
Ast *elem = elem_;
|
||||
if (elem->kind == Ast_FieldValue) {
|
||||
elem = elem->FieldValue.value;
|
||||
}
|
||||
elem = unparen_expr(elem);
|
||||
|
||||
Entity *e = entity_of_node(elem);
|
||||
if (elem->tav.mode != Addressing_Constant && e == nullptr && elem->kind != Ast_ProcLit) {
|
||||
Token tok = ast_token(elem);
|
||||
TokenPos pos = tok.pos;
|
||||
gbString s = type_to_string(type_of_expr(elem));
|
||||
error_line("%s Element is not constant, which is required for @(rodata), of type %s\n", token_pos_to_string(pos), s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
|
||||
@@ -1915,7 +1980,7 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
|
||||
rw_mutex_shared_lock(&decl->deps_mutex);
|
||||
rw_mutex_lock(&decl->parent->deps_mutex);
|
||||
|
||||
for (Entity *e : decl->deps) {
|
||||
FOR_PTR_SET(e, decl->deps) {
|
||||
ptr_set_add(&decl->parent->deps, e);
|
||||
}
|
||||
|
||||
@@ -1967,8 +2032,8 @@ 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 (decl->parent && decl->entity.load() && decl->parent->entity) {
|
||||
decl->entity.load()->parent_proc_decl = decl->parent;
|
||||
}
|
||||
|
||||
if (ctx->pkg->name != "runtime") {
|
||||
@@ -1981,9 +2046,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
|
||||
ast_node(bs, BlockStmt, body);
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
Array<ProcUsingVar> using_entities = {};
|
||||
using_entities.allocator = heap_allocator();
|
||||
defer (array_free(&using_entities));
|
||||
using_entities.allocator = temporary_allocator();
|
||||
|
||||
{
|
||||
if (type->Proc.param_count > 0) {
|
||||
@@ -2072,7 +2137,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked);
|
||||
if (decl->defer_use_checked) {
|
||||
GB_ASSERT(is_type_polymorphic(type, true));
|
||||
error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string));
|
||||
error(token, "Defer Use Checked: %.*s", LIT(decl->entity.load()->token.string));
|
||||
GB_ASSERT(decl->defer_use_checked == false);
|
||||
}
|
||||
|
||||
|
||||
+231
-160
@@ -587,6 +587,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
d->proc_lit = proc_lit;
|
||||
d->proc_checked_state = ProcCheckedState_Unchecked;
|
||||
d->defer_use_checked = false;
|
||||
d->para_poly_original = old_decl->entity;
|
||||
|
||||
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
|
||||
entity->state.store(EntityState_Resolved);
|
||||
@@ -608,7 +609,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
entity->flags |= EntityFlag_Disabled;
|
||||
}
|
||||
|
||||
d->entity = entity;
|
||||
d->entity.store(entity);
|
||||
|
||||
AstFile *file = nullptr;
|
||||
{
|
||||
@@ -821,9 +822,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
|
||||
if (c->in_enum_type) {
|
||||
return 3;
|
||||
if (c != nullptr) {
|
||||
if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
|
||||
if (c->in_enum_type) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1293,6 +1296,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
error_line("\t Got: %s\n", s_got);
|
||||
gb_string_free(s_got);
|
||||
gb_string_free(s_expected);
|
||||
|
||||
Type *tx = x->Proc.params->Tuple.variables[0]->type;
|
||||
Type *ty = y->Proc.params->Tuple.variables[0]->type;
|
||||
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(tx), LIT(type_strings[tx->kind]), tx->Named.type_name, tx->Named.type_name->TypeName.original_type_for_parapoly);
|
||||
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(ty), LIT(type_strings[ty->kind]), ty->Named.type_name, ty->Named.type_name->TypeName.original_type_for_parapoly);
|
||||
} else {
|
||||
gbString s_expected = type_to_string(y);
|
||||
gbString s_got = type_to_string(x);
|
||||
@@ -1738,7 +1746,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
|
||||
o->expr = n;
|
||||
String name = n->Ident.token.string;
|
||||
|
||||
Entity *e = scope_lookup(c->scope, name);
|
||||
Entity *e = scope_lookup(c->scope, name, n->Ident.hash);
|
||||
if (e == nullptr) {
|
||||
if (is_blank_ident(name)) {
|
||||
error(n, "'_' cannot be used as a value");
|
||||
@@ -2333,6 +2341,20 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
|
||||
if (in_value.kind == ExactValue_Integer) {
|
||||
return true;
|
||||
}
|
||||
} else if (is_type_typeid(type)) {
|
||||
|
||||
if (in_value.kind == ExactValue_Compound) {
|
||||
ast_node(cl, CompoundLit, in_value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
in_value = exact_value_typeid(nullptr);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (in_value.kind == ExactValue_Typeid) {
|
||||
if (out_value) *out_value = in_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -3503,6 +3525,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
|
||||
bool is_const_expr = x->mode == Addressing_Constant;
|
||||
|
||||
|
||||
Type *bt = base_type(type);
|
||||
if (is_const_expr && is_type_constant_type(bt)) {
|
||||
if (core_type(bt)->kind == Type_Basic) {
|
||||
@@ -3524,6 +3547,9 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
|
||||
} else if (is_type_slice(type) && is_type_string(x->type)) {
|
||||
x->mode = Addressing_Value;
|
||||
} else if (is_type_union(type)) {
|
||||
if (is_type_union_constantable(type)) {
|
||||
return true;
|
||||
}
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
if (x->mode == Addressing_Value) {
|
||||
@@ -4639,7 +4665,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
|
||||
}
|
||||
|
||||
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
|
||||
// GB_ASSERT_NOT_NULL(target_type);
|
||||
if (target_type == nullptr || operand->mode == Addressing_Invalid ||
|
||||
operand->mode == Addressing_Type ||
|
||||
is_type_typed(operand->type) ||
|
||||
@@ -4810,7 +4835,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
isize count = t->Union.variants.count;
|
||||
ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
|
||||
ValidIndexAndScore *valids = temporary_alloc_array<ValidIndexAndScore>(count);
|
||||
isize valid_count = 0;
|
||||
isize first_success_index = -1;
|
||||
for_array(i, t->Union.variants) {
|
||||
@@ -4851,7 +4876,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
break;
|
||||
}
|
||||
operand->type = new_type;
|
||||
operand->mode = Addressing_Value;
|
||||
if (operand->mode != Addressing_Constant ||
|
||||
!elem_type_can_be_constant(operand->type)) {
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
break;
|
||||
} else if (valid_count > 1) {
|
||||
ERROR_BLOCK();
|
||||
@@ -5093,7 +5121,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
|
||||
}
|
||||
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
if (is_type_struct(node->tav.type)) {
|
||||
if (is_type_raw_union(node->tav.type)) {
|
||||
if (success_) *success_ = false;
|
||||
if (finish_) *finish_ = true;
|
||||
return empty_exact_value;
|
||||
} else if (is_type_struct(node->tav.type)) {
|
||||
bool found = false;
|
||||
for (Ast *elem : cl->elems) {
|
||||
if (elem->kind != Ast_FieldValue) {
|
||||
@@ -5102,7 +5134,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
|
||||
ast_node(fv, FieldValue, elem);
|
||||
String name = fv->field->Ident.token.string;
|
||||
Selection sub_sel = lookup_field(node->tav.type, name, false);
|
||||
defer (array_free(&sub_sel.index));
|
||||
if (sub_sel.index.count > 0 &&
|
||||
sub_sel.index[0] == index) {
|
||||
value = fv->value->tav.value;
|
||||
@@ -5363,7 +5394,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
|
||||
}
|
||||
} else */if (node->kind == Ast_Ident) {
|
||||
String name = node->Ident.token.string;
|
||||
return scope_lookup(c->scope, name);
|
||||
return scope_lookup(c->scope, name, node->Ident.hash);
|
||||
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
|
||||
ast_node(se, SelectorExpr, node);
|
||||
if (se->token.kind == Token_ArrowRight) {
|
||||
@@ -5385,7 +5416,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
|
||||
|
||||
if (op_expr->kind == Ast_Ident) {
|
||||
String op_name = op_expr->Ident.token.string;
|
||||
Entity *e = scope_lookup(c->scope, op_name);
|
||||
Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
|
||||
if (e == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -5482,7 +5513,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
|
||||
if (op_expr->kind == Ast_Ident) {
|
||||
String op_name = op_expr->Ident.token.string;
|
||||
Entity *e = scope_lookup(c->scope, op_name);
|
||||
Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
|
||||
add_entity_use(c, op_expr, e);
|
||||
expr_entity = e;
|
||||
|
||||
@@ -5808,7 +5839,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
|
||||
switch (entity->kind) {
|
||||
case Entity_Constant:
|
||||
operand->value = entity->Constant.value;
|
||||
operand->value = entity->Constant.value;
|
||||
operand->mode = Addressing_Constant;
|
||||
if (operand->value.kind == ExactValue_Procedure) {
|
||||
Entity *proc = strip_entity_wrapping(operand->value.value_procedure);
|
||||
@@ -5905,7 +5936,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Entity *e = scope_lookup(s, name);
|
||||
Entity *e = scope_lookup(s, name, i->hash);
|
||||
if (e != nullptr) {
|
||||
if (out_scope) *out_scope = e->scope;
|
||||
return true;
|
||||
@@ -6029,7 +6060,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
|
||||
}
|
||||
rw_mutex_shared_lock(&decl->deps_mutex);
|
||||
rw_mutex_lock(&c->decl->deps_mutex);
|
||||
for (Entity *dep : decl->deps) {
|
||||
FOR_PTR_SET(dep, decl->deps) {
|
||||
ptr_set_add(&c->decl->deps, dep);
|
||||
}
|
||||
rw_mutex_unlock(&c->decl->deps_mutex);
|
||||
@@ -6257,7 +6288,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
|
||||
GB_ASSERT(ce->split_args);
|
||||
auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
|
||||
auto visited = temporary_slice_make<bool>(pt->param_count);
|
||||
auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
|
||||
defer ({
|
||||
for (Operand const &o : ordered_operands) {
|
||||
@@ -6473,7 +6504,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
|
||||
if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
|
||||
if (o->mode != Addressing_Constant) {
|
||||
bool ok = false;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
ok = true;
|
||||
} else if (o->value.kind == ExactValue_Procedure) {
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (show_error) {
|
||||
error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string));
|
||||
}
|
||||
@@ -6972,6 +7010,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
array_unordered_remove(&procs, proc_index);
|
||||
continue;
|
||||
}
|
||||
if (!pt->Proc.variadic && max_arg_count != ISIZE_MAX && param_count < max_arg_count) {
|
||||
array_unordered_remove(&procs, proc_index);
|
||||
continue;
|
||||
}
|
||||
proc_index++;
|
||||
}
|
||||
}
|
||||
@@ -6980,10 +7022,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
isize lhs_count = -1;
|
||||
i32 variadic_index = -1;
|
||||
|
||||
auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
defer (array_free(&positional_operands));
|
||||
defer (array_free(&named_operands));
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
auto positional_operands = array_make<Operand>(temporary_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
|
||||
|
||||
if (procs.count == 1) {
|
||||
Entity *e = procs[0];
|
||||
@@ -7024,7 +7066,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
if (proc_arg_count >= 0) {
|
||||
lhs_count = proc_arg_count;
|
||||
if (lhs_count > 0) {
|
||||
lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
|
||||
lhs = gb_alloc_array(temporary_allocator(), Entity *, lhs_count);
|
||||
for (isize param_index = 0; param_index < lhs_count; param_index++) {
|
||||
Entity *e = nullptr;
|
||||
for (Entity *p : procs) {
|
||||
@@ -7110,13 +7152,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
array_add(&named_operands, o);
|
||||
}
|
||||
|
||||
gb_free(heap_allocator(), lhs);
|
||||
auto valids = array_make<ValidIndexAndScore>(temporary_allocator(), 0, procs.count);
|
||||
|
||||
auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
|
||||
defer (array_free(&valids));
|
||||
|
||||
auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
|
||||
defer (array_free(&proc_entities));
|
||||
auto proc_entities = array_make<Entity *>(temporary_allocator(), 0, procs.count*2 + 1);
|
||||
for (Entity *proc : procs) {
|
||||
array_add(&proc_entities, proc);
|
||||
}
|
||||
@@ -7247,7 +7285,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
}
|
||||
|
||||
// Try to reduce the list further for `$T: typeid` like parameters
|
||||
bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
|
||||
bool *possibly_ignore = temporary_alloc_array<bool>(procs.count);
|
||||
isize possibly_ignore_set = 0;
|
||||
|
||||
if (true) {
|
||||
@@ -7335,7 +7373,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
}
|
||||
|
||||
isize max_spaces = gb_max(max_name_length, max_type_length);
|
||||
char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
|
||||
char *spaces = temporary_alloc_array<char>(max_spaces+1);
|
||||
for (isize i = 0; i < max_spaces; i++) {
|
||||
spaces[i] = ' ';
|
||||
}
|
||||
@@ -7510,11 +7548,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
|
||||
return check_call_arguments_proc_group(c, operand, call);
|
||||
}
|
||||
|
||||
auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
|
||||
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
defer (array_free(&positional_operands));
|
||||
defer (array_free(&named_operands));
|
||||
auto positional_operands = array_make<Operand>(temporary_allocator(), 0, positional_args.count);
|
||||
auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
|
||||
|
||||
if (positional_args.count > 0) {
|
||||
Entity **lhs = nullptr;
|
||||
@@ -7601,6 +7638,8 @@ gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_
|
||||
|
||||
|
||||
gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
ast_node(ce, CallExpr, call);
|
||||
|
||||
Type *original_type = operand->type;
|
||||
@@ -7609,7 +7648,6 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
bool show_error = true;
|
||||
|
||||
Array<Operand> operands = {};
|
||||
defer (array_free(&operands));
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
|
||||
@@ -7617,15 +7655,14 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
{
|
||||
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
|
||||
auto prev_type_path = c->type_path;
|
||||
c->type_path = new_checker_type_path();
|
||||
defer ({
|
||||
destroy_checker_type_path(c->type_path);
|
||||
c->type_path = prev_type_path;
|
||||
});
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
c->type_path = new_checker_type_path(temporary_allocator());
|
||||
defer (c->type_path = prev_type_path);
|
||||
|
||||
if (is_call_expr_field_value(ce)) {
|
||||
named_fields = true;
|
||||
operands = array_make<Operand>(heap_allocator(), ce->args.count);
|
||||
operands = array_make<Operand>(temporary_allocator(), ce->args.count);
|
||||
for_array(i, ce->args) {
|
||||
Ast *arg = ce->args[i];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
@@ -7657,7 +7694,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
}
|
||||
|
||||
} else {
|
||||
operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count);
|
||||
operands = array_make<Operand>(temporary_allocator(), 0, 2*ce->args.count);
|
||||
|
||||
Entity **lhs = nullptr;
|
||||
isize lhs_count = -1;
|
||||
@@ -7699,7 +7736,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
} else {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
|
||||
bool *visited = temporary_alloc_array<bool>(param_count);
|
||||
|
||||
// LEAK(bill)
|
||||
ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
|
||||
@@ -7883,11 +7920,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
|
||||
{
|
||||
GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
|
||||
|
||||
mutex_lock(&found_gen_types->mutex);
|
||||
defer (mutex_unlock(&found_gen_types->mutex));
|
||||
Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
|
||||
|
||||
Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
|
||||
if (found_entity) {
|
||||
operand->mode = Addressing_Type;
|
||||
operand->type = found_entity->type;
|
||||
@@ -8015,6 +8051,106 @@ gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char con
|
||||
return Expr_Stmt; \
|
||||
}
|
||||
|
||||
gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *operand, Ast *call, Slice<Ast *> const &args, Type *type_hint) {
|
||||
GB_ASSERT(operand->mode == Addressing_Type);
|
||||
Type *t = operand->type;
|
||||
if (is_type_polymorphic_record(t)) {
|
||||
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
|
||||
|
||||
if (!is_type_named(t)) {
|
||||
gbString s = expr_to_string(operand->expr);
|
||||
error(call, "Illegal use of an unnamed polymorphic record, %s", s);
|
||||
gb_string_free(s);
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->type = t_invalid;;
|
||||
return Expr_Expr;
|
||||
}
|
||||
auto err = check_polymorphic_record_type(c, operand, call);
|
||||
if (err == 0) {
|
||||
Ast *ident = operand->expr;
|
||||
while (ident->kind == Ast_SelectorExpr) {
|
||||
Ast *s = ident->SelectorExpr.selector;
|
||||
ident = s;
|
||||
}
|
||||
Type *ot = operand->type;
|
||||
GB_ASSERT(ot->kind == Type_Named);
|
||||
Entity *e = ot->Named.type_name;
|
||||
add_entity_use(c, ident, e);
|
||||
add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value);
|
||||
} else {
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->type = t_invalid;
|
||||
}
|
||||
} else {
|
||||
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
isize arg_count = args.count;
|
||||
switch (arg_count) {
|
||||
case 0:
|
||||
{
|
||||
gbString str = type_to_string(t);
|
||||
error(call, "Missing argument in conversion to '%s'", str);
|
||||
gb_string_free(str);
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
gbString str = type_to_string(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
ERROR_BLOCK();
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_complex32:
|
||||
case Basic_complex64:
|
||||
case Basic_complex128:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str);
|
||||
break;
|
||||
case Basic_quaternion64:
|
||||
case Basic_quaternion128:
|
||||
case Basic_quaternion256:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str);
|
||||
break;
|
||||
default:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
}
|
||||
} else {
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
}
|
||||
gb_string_free(str);
|
||||
} break;
|
||||
case 1: {
|
||||
Ast *arg = args[0];
|
||||
if (arg->kind == Ast_FieldValue) {
|
||||
error(call, "'field = value' cannot be used in a type conversion");
|
||||
arg = arg->FieldValue.value;
|
||||
// NOTE(bill): Carry on the cast regardless
|
||||
}
|
||||
check_expr_with_type_hint(c, operand, arg, t);
|
||||
if (operand->mode != Addressing_Invalid) {
|
||||
if (is_type_polymorphic(t)) {
|
||||
error(call, "A polymorphic type cannot be used in a type conversion");
|
||||
} else {
|
||||
// NOTE(bill): Otherwise the compiler can override the polymorphic type
|
||||
// as it assumes it is determining the type
|
||||
check_cast(c, operand, t);
|
||||
}
|
||||
}
|
||||
operand->type = t;
|
||||
operand->expr = call;
|
||||
|
||||
|
||||
if (operand->mode != Addressing_Invalid) {
|
||||
update_untyped_expr_type(c, arg, t, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
|
||||
if (proc != nullptr &&
|
||||
@@ -8071,99 +8207,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Type) {
|
||||
Type *t = operand->type;
|
||||
if (is_type_polymorphic_record(t)) {
|
||||
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
|
||||
|
||||
if (!is_type_named(t)) {
|
||||
gbString s = expr_to_string(operand->expr);
|
||||
error(call, "Illegal use of an unnamed polymorphic record, %s", s);
|
||||
gb_string_free(s);
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->type = t_invalid;;
|
||||
return Expr_Expr;
|
||||
}
|
||||
auto err = check_polymorphic_record_type(c, operand, call);
|
||||
if (err == 0) {
|
||||
Ast *ident = operand->expr;
|
||||
while (ident->kind == Ast_SelectorExpr) {
|
||||
Ast *s = ident->SelectorExpr.selector;
|
||||
ident = s;
|
||||
}
|
||||
Type *ot = operand->type;
|
||||
GB_ASSERT(ot->kind == Type_Named);
|
||||
Entity *e = ot->Named.type_name;
|
||||
add_entity_use(c, ident, e);
|
||||
add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value);
|
||||
} else {
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->type = t_invalid;
|
||||
}
|
||||
} else {
|
||||
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
isize arg_count = args.count;
|
||||
switch (arg_count) {
|
||||
case 0:
|
||||
{
|
||||
gbString str = type_to_string(t);
|
||||
error(call, "Missing argument in conversion to '%s'", str);
|
||||
gb_string_free(str);
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
gbString str = type_to_string(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
ERROR_BLOCK();
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_complex32:
|
||||
case Basic_complex64:
|
||||
case Basic_complex128:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str);
|
||||
break;
|
||||
case Basic_quaternion64:
|
||||
case Basic_quaternion128:
|
||||
case Basic_quaternion256:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str);
|
||||
break;
|
||||
default:
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
}
|
||||
} else {
|
||||
error(call, "Too many arguments in conversion to '%s'", str);
|
||||
}
|
||||
gb_string_free(str);
|
||||
} break;
|
||||
case 1: {
|
||||
Ast *arg = args[0];
|
||||
if (arg->kind == Ast_FieldValue) {
|
||||
error(call, "'field = value' cannot be used in a type conversion");
|
||||
arg = arg->FieldValue.value;
|
||||
// NOTE(bill): Carry on the cast regardless
|
||||
}
|
||||
check_expr_with_type_hint(c, operand, arg, t);
|
||||
if (operand->mode != Addressing_Invalid) {
|
||||
if (is_type_polymorphic(t)) {
|
||||
error(call, "A polymorphic type cannot be used in a type conversion");
|
||||
} else {
|
||||
// NOTE(bill): Otherwise the compiler can override the polymorphic type
|
||||
// as it assumes it is determining the type
|
||||
check_cast(c, operand, t);
|
||||
}
|
||||
}
|
||||
operand->type = t;
|
||||
operand->expr = call;
|
||||
if (operand->mode != Addressing_Invalid) {
|
||||
update_untyped_expr_type(c, arg, t, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Expr_Expr;
|
||||
return check_call_expr_as_type_cast(c, operand, call, args, type_hint);
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
@@ -8335,9 +8379,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
if (c->curr_proc_decl == nullptr) {
|
||||
error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
|
||||
} else {
|
||||
GB_ASSERT(c->curr_proc_decl->entity);
|
||||
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
|
||||
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
|
||||
Entity *e = c->curr_proc_decl->entity.load();
|
||||
GB_ASSERT(e);
|
||||
GB_ASSERT(e->type->kind == Type_Proc);
|
||||
String scope_features = e->type->Proc.enable_target_feature;
|
||||
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
|
||||
ERROR_BLOCK();
|
||||
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
|
||||
@@ -8630,7 +8675,7 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Ope
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
|
||||
gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o, Type *field_type) {
|
||||
if (is_operand_nil(*o)) {
|
||||
return true;
|
||||
}
|
||||
@@ -8645,6 +8690,13 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
|
||||
add_type_info_type(c, o->type);
|
||||
return true;
|
||||
}
|
||||
if (is_type_any(field_type)) {
|
||||
return false;
|
||||
}
|
||||
return o->mode == Addressing_Constant;
|
||||
}
|
||||
|
||||
@@ -8874,7 +8926,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
|
||||
isize count = multi_map_count(seen, key);
|
||||
if (count) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
|
||||
TypeAndToken *taps = temporary_alloc_array<TypeAndToken>(count);
|
||||
|
||||
multi_map_get_all(seen, key, taps);
|
||||
for (isize i = 0; i < count; i++) {
|
||||
@@ -9595,8 +9647,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_constant &&
|
||||
(is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
|
||||
if (is_constant && elem_cannot_be_constant(ft)) {
|
||||
is_constant = false;
|
||||
}
|
||||
}
|
||||
@@ -9631,11 +9682,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
Operand o = {};
|
||||
check_expr_or_type(c, &o, fv->value, field->type);
|
||||
|
||||
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
|
||||
if (elem_cannot_be_constant(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o);
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
|
||||
}
|
||||
|
||||
u8 prev_bit_field_bit_size = c->bit_field_bit_size;
|
||||
@@ -9668,7 +9719,10 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
|
||||
}
|
||||
|
||||
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
|
||||
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
|
||||
if (cl->elems.count == 0) {
|
||||
return false;
|
||||
}
|
||||
if ((check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
|
||||
ERROR_BLOCK();
|
||||
error(node, "Compound literals of dynamic types are disabled by default");
|
||||
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
|
||||
@@ -9679,9 +9733,13 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
|
||||
error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
|
||||
}
|
||||
return false;
|
||||
} else if (c->curr_proc_decl != nullptr && c->curr_proc_calling_convention != ProcCC_Odin) {
|
||||
if (c->scope != nullptr && (c->scope->flags & ScopeFlag_ContextDefined) == 0) {
|
||||
error(node, "Compound literals of dynamic types require a 'context' to defined");
|
||||
}
|
||||
}
|
||||
|
||||
return cl->elems.count > 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
|
||||
@@ -9801,7 +9859,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
if (t->Struct.is_raw_union) {
|
||||
if (cl->elems.count > 0) {
|
||||
// NOTE: unions cannot be constant
|
||||
is_constant = false;
|
||||
is_constant = elem_type_can_be_constant(t);
|
||||
|
||||
if (cl->elems[0]->kind != Ast_FieldValue) {
|
||||
gbString type_str = type_to_string(type);
|
||||
@@ -9861,11 +9919,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
Operand o = {};
|
||||
check_expr_or_type(c, &o, elem, field->type);
|
||||
|
||||
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
|
||||
if (elem_cannot_be_constant(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o);
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
|
||||
}
|
||||
|
||||
check_assignment(c, &o, field->type, str_lit("structure literal"));
|
||||
@@ -10010,7 +10068,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
} else {
|
||||
Operand op_index = {};
|
||||
check_expr(c, &op_index, fv->field);
|
||||
@@ -10042,7 +10102,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10069,7 +10131,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, e, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (max < index) {
|
||||
@@ -10243,7 +10307,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
|
||||
TokenKind upper_op = Token_LtEq;
|
||||
if (op.kind == Token_RangeHalf) {
|
||||
@@ -10284,7 +10350,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
|
||||
add_to_seen_map(c, &seen, op_index);
|
||||
}
|
||||
@@ -10314,7 +10382,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
check_expr_with_type_hint(c, &operand, e, elem_type);
|
||||
check_assignment(c, &operand, elem_type, context_name);
|
||||
|
||||
is_constant = is_constant && operand.mode == Addressing_Constant;
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (max < index) {
|
||||
@@ -10920,7 +10990,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
|
||||
}
|
||||
}
|
||||
|
||||
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
|
||||
auto modified_args = permanent_slice_make<Ast *>(ce->args.count+1);
|
||||
modified_args[0] = first_arg;
|
||||
slice_copy(&modified_args, ce->args, 1);
|
||||
ce->args = modified_args;
|
||||
@@ -11423,6 +11493,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
|
||||
o->mode = Addressing_Value;
|
||||
o->type = type;
|
||||
o->value = exact_value_procedure(node);
|
||||
case_end;
|
||||
|
||||
case_ast_node(te, TernaryIfExpr, node);
|
||||
|
||||
+84
-63
@@ -484,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
}
|
||||
if (ident_node != nullptr) {
|
||||
ast_node(i, Ident, ident_node);
|
||||
e = scope_lookup(ctx->scope, i->token.string);
|
||||
e = scope_lookup(ctx->scope, i->token.string, i->hash);
|
||||
if (e != nullptr && e->kind == Entity_Variable) {
|
||||
used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
|
||||
}
|
||||
@@ -1812,8 +1812,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed");
|
||||
}
|
||||
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
|
||||
String name = rs->vals[0]->Ident.token.string;
|
||||
Entity *found = scope_lookup(ctx->scope, name);
|
||||
AstIdent *ident = &rs->vals[0]->Ident;
|
||||
String name = ident->token.string;
|
||||
Entity *found = scope_lookup(ctx->scope, name, ident->hash);
|
||||
if (found && are_types_identical(found->type, t->BitSet.elem)) {
|
||||
ERROR_BLOCK();
|
||||
gbString s = expr_to_string(expr);
|
||||
@@ -1857,8 +1858,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
error(node, "#reverse for is not supported for map types, as maps are unordered");
|
||||
}
|
||||
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
|
||||
String name = rs->vals[0]->Ident.token.string;
|
||||
Entity *found = scope_lookup(ctx->scope, name);
|
||||
AstIdent *ident = &rs->vals[0]->Ident;
|
||||
String name = ident->token.string;
|
||||
Entity *found = scope_lookup(ctx->scope, name, ident->hash);
|
||||
if (found && are_types_identical(found->type, t->Map.key)) {
|
||||
ERROR_BLOCK();
|
||||
gbString s = expr_to_string(expr);
|
||||
@@ -1963,7 +1965,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
}
|
||||
|
||||
auto rhs = slice_from_array(vals);
|
||||
auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count);
|
||||
auto lhs = temporary_slice_make<Ast *>(rhs.count);
|
||||
slice_copy(&lhs, rs->vals);
|
||||
|
||||
isize addressable_index = cast(isize)is_map;
|
||||
@@ -2539,6 +2541,78 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
check_close_scope(ctx);
|
||||
}
|
||||
|
||||
// NOTE(bill): This is very basic escape analysis
|
||||
// This needs to be improved tremendously, and a lot of it done during the
|
||||
// middle-end (or LLVM side) to improve checks and error messages
|
||||
void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
|
||||
auto const unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
|
||||
gbString s = expr_to_string(o.expr);
|
||||
if (extra_type) {
|
||||
gbString t = type_to_string(extra_type);
|
||||
error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
|
||||
gb_string_free(t);
|
||||
} else {
|
||||
error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
|
||||
}
|
||||
gb_string_free(s);
|
||||
};
|
||||
|
||||
if (type == nullptr || expr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (expr->kind == Ast_CompoundLit && is_type_slice(type)) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
if (cl->elems.count == 0) {
|
||||
return;
|
||||
}
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
|
||||
Ast *x = unparen_expr(expr->UnaryExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e)) {
|
||||
unsafe_return_error(o, "the address of a local variable");
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "the address of a compound literal");
|
||||
} else if (x->kind == Ast_IndexExpr) {
|
||||
Entity *f = entity_of_node(x->IndexExpr.expr);
|
||||
if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
|
||||
if (is_entity_local_variable(f)) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (x->kind == Ast_MatrixIndexExpr) {
|
||||
Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
|
||||
if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (expr->kind == Ast_SliceExpr) {
|
||||
Ast *x = unparen_expr(expr->SliceExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e) && is_type_array(e->type)) {
|
||||
unsafe_return_error(o, "a slice of a local variable");
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "a slice of a compound literal");
|
||||
}
|
||||
} else if (o.mode == Addressing_Constant && is_type_slice(type)) {
|
||||
ERROR_BLOCK();
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
|
||||
} else if (expr->kind == Ast_CompoundLit) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
for (Ast *elem : cl->elems) {
|
||||
if (elem->kind == Ast_FieldValue) {
|
||||
ast_node(fv, FieldValue, elem);
|
||||
Entity *e = entity_of_node(fv->field);
|
||||
if (e != nullptr) {
|
||||
check_unsafe_return(o, e->type, fv->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
ast_node(rs, ReturnStmt, node);
|
||||
|
||||
@@ -2567,8 +2641,9 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
result_count = proc_type->Proc.results->Tuple.variables.count;
|
||||
}
|
||||
|
||||
auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
|
||||
defer (array_free(&operands));
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
auto operands = array_make<Operand>(temporary_allocator(), 0, 2*rs->results.count);
|
||||
|
||||
check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk);
|
||||
|
||||
@@ -2614,61 +2689,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
expr = unparen_expr(arg);
|
||||
}
|
||||
|
||||
auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
|
||||
gbString s = expr_to_string(o.expr);
|
||||
if (extra_type) {
|
||||
gbString t = type_to_string(extra_type);
|
||||
error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
|
||||
gb_string_free(t);
|
||||
} else {
|
||||
error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
|
||||
}
|
||||
gb_string_free(s);
|
||||
};
|
||||
|
||||
|
||||
// NOTE(bill): This is very basic escape analysis
|
||||
// This needs to be improved tremendously, and a lot of it done during the
|
||||
// middle-end (or LLVM side) to improve checks and error messages
|
||||
if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
if (cl->elems.count == 0) {
|
||||
continue;
|
||||
}
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
|
||||
Ast *x = unparen_expr(expr->UnaryExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e)) {
|
||||
unsafe_return_error(o, "the address of a local variable");
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "the address of a compound literal");
|
||||
} else if (x->kind == Ast_IndexExpr) {
|
||||
Entity *f = entity_of_node(x->IndexExpr.expr);
|
||||
if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
|
||||
if (is_entity_local_variable(f)) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (x->kind == Ast_MatrixIndexExpr) {
|
||||
Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
|
||||
if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (expr->kind == Ast_SliceExpr) {
|
||||
Ast *x = unparen_expr(expr->SliceExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e) && is_type_array(e->type)) {
|
||||
unsafe_return_error(o, "a slice of a local variable");
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "a slice of a compound literal");
|
||||
}
|
||||
} else if (o.mode == Addressing_Constant && is_type_slice(o.type)) {
|
||||
ERROR_BLOCK();
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
|
||||
}
|
||||
check_unsafe_return(o, o.type, expr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-25
@@ -267,19 +267,19 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_,
|
||||
|
||||
|
||||
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
|
||||
mutex_lock(&ctx->info->gen_types_mutex); // @@global
|
||||
|
||||
GenTypesData *found_gen_types = nullptr;
|
||||
auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
|
||||
if (found_gen_types_ptr == nullptr) {
|
||||
|
||||
GB_ASSERT(original_type->kind == Type_Named);
|
||||
mutex_lock(&original_type->Named.gen_types_data_mutex);
|
||||
if (original_type->Named.gen_types_data == nullptr) {
|
||||
GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData);
|
||||
gen_types->types = array_make<Entity *>(heap_allocator());
|
||||
map_set(&ctx->info->gen_types, original_type, gen_types);
|
||||
found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
|
||||
original_type->Named.gen_types_data = gen_types;
|
||||
}
|
||||
found_gen_types = *found_gen_types_ptr;
|
||||
GB_ASSERT(found_gen_types != nullptr);
|
||||
mutex_unlock(&ctx->info->gen_types_mutex); // @@global
|
||||
found_gen_types = original_type->Named.gen_types_data;
|
||||
|
||||
mutex_unlock(&original_type->Named.gen_types_data_mutex);
|
||||
|
||||
return found_gen_types;
|
||||
}
|
||||
|
||||
@@ -312,6 +312,7 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
|
||||
e->state = EntityState_Resolved;
|
||||
e->file = ctx->file;
|
||||
e->pkg = pkg;
|
||||
e->TypeName.original_type_for_parapoly = original_type;
|
||||
add_entity_use(ctx, node, e);
|
||||
}
|
||||
|
||||
@@ -1106,7 +1107,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
|
||||
|
||||
GB_ASSERT(fields.count <= bf->fields.count);
|
||||
|
||||
auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
|
||||
auto bit_offsets = permanent_slice_make<i64>(fields.count);
|
||||
i64 curr_offset = 0;
|
||||
for_array(i, bit_sizes) {
|
||||
bit_offsets[i] = curr_offset;
|
||||
@@ -2707,7 +2708,7 @@ gb_internal Type *get_map_cell_type(Type *type) {
|
||||
// Padding exists
|
||||
Type *s = alloc_type_struct();
|
||||
Scope *scope = create_scope(nullptr, nullptr);
|
||||
s->Struct.fields = slice_make<Entity *>(permanent_allocator(), 2);
|
||||
s->Struct.fields = permanent_slice_make<Entity *>(2);
|
||||
s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
|
||||
s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
|
||||
s->Struct.scope = scope;
|
||||
@@ -2732,7 +2733,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
|
||||
|
||||
Type *metadata_type = alloc_type_struct();
|
||||
Scope *metadata_scope = create_scope(nullptr, nullptr);
|
||||
metadata_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 5);
|
||||
metadata_type->Struct.fields = permanent_slice_make<Entity *>(5);
|
||||
metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved);
|
||||
metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved);
|
||||
metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved);
|
||||
@@ -2750,7 +2751,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
|
||||
|
||||
Scope *scope = create_scope(nullptr, nullptr);
|
||||
Type *debug_type = alloc_type_struct();
|
||||
debug_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 3);
|
||||
debug_type->Struct.fields = permanent_slice_make<Entity *>(3);
|
||||
debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved);
|
||||
debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved);
|
||||
debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved);
|
||||
@@ -2983,13 +2984,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
|
||||
if (wait_to_finish) {
|
||||
wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
|
||||
} else {
|
||||
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load());
|
||||
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0);
|
||||
}
|
||||
|
||||
field_count = old_struct->Struct.fields.count;
|
||||
|
||||
t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
|
||||
t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
|
||||
t->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
|
||||
t->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
|
||||
|
||||
|
||||
auto const &add_entity = [](Scope *scope, Entity *entity) {
|
||||
@@ -3107,7 +3108,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
|
||||
if (is_polymorphic) {
|
||||
field_count = 0;
|
||||
|
||||
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
|
||||
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
|
||||
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
|
||||
soa_struct->Struct.soa_count = 0;
|
||||
|
||||
@@ -3117,7 +3118,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
|
||||
Type *old_array = base_type(elem);
|
||||
field_count = cast(isize)old_array->Array.count;
|
||||
|
||||
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
|
||||
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
|
||||
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
|
||||
|
||||
string_map_init(&scope->elements, 8);
|
||||
@@ -3159,8 +3160,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
|
||||
if (old_struct->Struct.fields_wait_signal.futex.load()) {
|
||||
field_count = old_struct->Struct.fields.count;
|
||||
|
||||
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
|
||||
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
|
||||
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
|
||||
soa_struct->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
|
||||
|
||||
for_array(i, old_struct->Struct.fields) {
|
||||
Entity *old_field = old_struct->Struct.fields[i];
|
||||
@@ -3355,7 +3356,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
|
||||
}
|
||||
}
|
||||
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
|
||||
GB_ASSERT_NOT_NULL(type);
|
||||
GB_ASSERT(type != nullptr);
|
||||
if (e == nullptr) {
|
||||
*type = t_invalid;
|
||||
return true;
|
||||
@@ -3512,8 +3513,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
|
||||
|
||||
case_ast_node(pt, PointerType, e);
|
||||
CheckerContext c = *ctx;
|
||||
c.type_path = new_checker_type_path();
|
||||
defer (destroy_checker_type_path(c.type_path));
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
c.type_path = new_checker_type_path(temporary_allocator());
|
||||
|
||||
Type *elem = t_invalid;
|
||||
Operand o = {};
|
||||
@@ -3747,8 +3749,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
|
||||
|
||||
gb_internal Type *check_type(CheckerContext *ctx, Ast *e) {
|
||||
CheckerContext c = *ctx;
|
||||
c.type_path = new_checker_type_path();
|
||||
defer (destroy_checker_type_path(c.type_path));
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
c.type_path = new_checker_type_path(temporary_allocator());
|
||||
|
||||
return check_type_expr(&c, e, nullptr);
|
||||
}
|
||||
|
||||
+435
-229
File diff suppressed because it is too large
Load Diff
+17
-14
@@ -209,7 +209,7 @@ struct DeclInfo {
|
||||
|
||||
Scope * scope;
|
||||
|
||||
Entity *entity;
|
||||
std::atomic<Entity *> entity;
|
||||
|
||||
Ast * decl_node;
|
||||
Ast * type_expr;
|
||||
@@ -218,6 +218,8 @@ struct DeclInfo {
|
||||
Ast * proc_lit; // Ast_ProcLit
|
||||
Type * gen_proc_type; // Precalculated
|
||||
|
||||
Entity * para_poly_original;
|
||||
|
||||
bool is_using;
|
||||
bool where_clauses_evaluated;
|
||||
bool foreign_require_results;
|
||||
@@ -309,11 +311,12 @@ struct EntityGraphNode;
|
||||
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
|
||||
|
||||
struct EntityGraphNode {
|
||||
Entity * entity; // Procedure, Variable, Constant
|
||||
Entity *entity; // Procedure, Variable, Constant
|
||||
|
||||
EntityGraphNodeSet pred;
|
||||
EntityGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
@@ -448,9 +451,11 @@ struct CheckerInfo {
|
||||
AstPackage * init_package;
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
BlockingMutex minimum_dependency_type_info_mutex;
|
||||
|
||||
RwMutex minimum_dependency_type_info_mutex;
|
||||
PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
|
||||
|
||||
RWSpinLock min_dep_type_info_set_mutex;
|
||||
TypeSet min_dep_type_info_set;
|
||||
Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count
|
||||
|
||||
@@ -477,8 +482,6 @@ struct CheckerInfo {
|
||||
|
||||
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
|
||||
|
||||
BlockingMutex gen_types_mutex;
|
||||
PtrMap<Type *, GenTypesData *> gen_types;
|
||||
|
||||
// BlockingMutex type_info_mutex; // NOT recursive
|
||||
// Array<TypeInfoPair> type_info_types;
|
||||
@@ -514,7 +517,7 @@ struct CheckerInfo {
|
||||
BlockingMutex load_file_mutex;
|
||||
StringMap<LoadFileCache *> load_file_cache;
|
||||
|
||||
BlockingMutex all_procedures_mutex;
|
||||
MPSCQueue<ProcInfo *> all_procedures_queue;
|
||||
Array<ProcInfo *> all_procedures;
|
||||
|
||||
BlockingMutex instrumentation_mutex;
|
||||
@@ -622,12 +625,12 @@ gb_internal Entity *entity_of_node(Ast *expr);
|
||||
|
||||
|
||||
gb_internal Entity *scope_lookup_current(Scope *s, String const &name);
|
||||
gb_internal Entity *scope_lookup (Scope *s, String const &name);
|
||||
gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
|
||||
gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0);
|
||||
gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0);
|
||||
gb_internal Entity *scope_insert (Scope *s, Entity *entity);
|
||||
|
||||
|
||||
gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value);
|
||||
gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex=true);
|
||||
gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr);
|
||||
gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value);
|
||||
gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
||||
@@ -648,8 +651,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
|
||||
gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
||||
gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
|
||||
|
||||
gb_internal CheckerTypePath *new_checker_type_path();
|
||||
gb_internal void destroy_checker_type_path(CheckerTypePath *tp);
|
||||
gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator);
|
||||
gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator);
|
||||
|
||||
gb_internal void check_type_path_push(CheckerContext *c, Entity *e);
|
||||
gb_internal Entity *check_type_path_pop (CheckerContext *c);
|
||||
|
||||
+54
-4
@@ -113,6 +113,13 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *arena_alloc_item(Arena *arena) {
|
||||
return cast(T *)arena_alloc(arena, gb_size_of(T), gb_align_of(T));
|
||||
}
|
||||
|
||||
|
||||
gb_internal void arena_free_all(Arena *arena) {
|
||||
while (arena->curr_block != nullptr) {
|
||||
MemoryBlock *free_block = arena->curr_block;
|
||||
@@ -346,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) {
|
||||
gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
|
||||
void *ptr = nullptr;
|
||||
Arena *arena = cast(Arena *)allocator_data;
|
||||
GB_ASSERT_NOT_NULL(arena);
|
||||
GB_ASSERT(arena != nullptr);
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc:
|
||||
@@ -394,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) {
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *permanent_alloc_item() {
|
||||
Arena *arena = get_arena(ThreadArena_Permanent);
|
||||
return arena_alloc_item<T>(arena);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *permanent_alloc_array(isize count) {
|
||||
Arena *arena = get_arena(ThreadArena_Permanent);
|
||||
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal Slice<T> permanent_slice_make(isize count) {
|
||||
Arena *arena = get_arena(ThreadArena_Permanent);
|
||||
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
|
||||
return {data, count};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *temporary_alloc_item() {
|
||||
Arena *arena = get_arena(ThreadArena_Temporary);
|
||||
return arena_alloc_item<T>(arena);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *temporary_alloc_array(isize count) {
|
||||
Arena *arena = get_arena(ThreadArena_Temporary);
|
||||
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal Slice<T> temporary_slice_make(isize count) {
|
||||
Arena *arena = get_arena(ThreadArena_Temporary);
|
||||
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
|
||||
return {data, count};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
|
||||
void *ptr = nullptr;
|
||||
@@ -432,15 +481,16 @@ gb_internal gbAllocator permanent_allocator() {
|
||||
}
|
||||
|
||||
gb_internal gbAllocator temporary_allocator() {
|
||||
return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
|
||||
// return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Temporary};
|
||||
return permanent_allocator();
|
||||
}
|
||||
|
||||
|
||||
#define TEMP_ARENA_GUARD(arena) ArenaTempGuard GB_DEFER_3(_arena_guard_){arena}
|
||||
|
||||
|
||||
// #define TEMPORARY_ALLOCATOR_GUARD()
|
||||
#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
|
||||
// #define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
|
||||
#define TEMPORARY_ALLOCATOR_GUARD()
|
||||
#define PERMANENT_ALLOCATOR_GUARD()
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -164,6 +164,7 @@ struct Entity {
|
||||
u64 id;
|
||||
std::atomic<u64> flags;
|
||||
std::atomic<EntityState> state;
|
||||
std::atomic<i32> min_dep_count;
|
||||
Token token;
|
||||
Scope * scope;
|
||||
Type * type;
|
||||
@@ -233,6 +234,7 @@ struct Entity {
|
||||
} Variable;
|
||||
struct {
|
||||
Type * type_parameter_specialization;
|
||||
Type * original_type_for_parapoly;
|
||||
String ir_mangled_name;
|
||||
bool is_type_alias;
|
||||
bool objc_is_implementation;
|
||||
@@ -347,7 +349,7 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ
|
||||
entity->type = type;
|
||||
entity->id = 1 + global_entity_id.fetch_add(1);
|
||||
if (token.pos.file_id) {
|
||||
entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id);
|
||||
entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
+19
-8
@@ -86,7 +86,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos);
|
||||
gb_internal bool set_file_path_string(i32 index, String const &path) {
|
||||
bool ok = false;
|
||||
GB_ASSERT(index >= 0);
|
||||
mutex_lock(&global_error_collector.path_mutex);
|
||||
// mutex_lock(&global_error_collector.path_mutex);
|
||||
mutex_lock(&global_files_mutex);
|
||||
|
||||
if (index >= global_file_path_strings.count) {
|
||||
@@ -99,14 +99,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) {
|
||||
}
|
||||
|
||||
mutex_unlock(&global_files_mutex);
|
||||
mutex_unlock(&global_error_collector.path_mutex);
|
||||
// mutex_unlock(&global_error_collector.path_mutex);
|
||||
return ok;
|
||||
}
|
||||
|
||||
gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
|
||||
bool ok = false;
|
||||
GB_ASSERT(index >= 0);
|
||||
mutex_lock(&global_error_collector.path_mutex);
|
||||
// mutex_lock(&global_error_collector.path_mutex);
|
||||
mutex_lock(&global_files_mutex);
|
||||
|
||||
if (index >= global_files.count) {
|
||||
@@ -118,13 +118,13 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
|
||||
ok = true;
|
||||
}
|
||||
mutex_unlock(&global_files_mutex);
|
||||
mutex_unlock(&global_error_collector.path_mutex);
|
||||
// mutex_unlock(&global_error_collector.path_mutex);
|
||||
return ok;
|
||||
}
|
||||
|
||||
gb_internal String get_file_path_string(i32 index) {
|
||||
GB_ASSERT(index >= 0);
|
||||
mutex_lock(&global_error_collector.path_mutex);
|
||||
// mutex_lock(&global_error_collector.path_mutex);
|
||||
mutex_lock(&global_files_mutex);
|
||||
|
||||
String path = {};
|
||||
@@ -133,13 +133,13 @@ gb_internal String get_file_path_string(i32 index) {
|
||||
}
|
||||
|
||||
mutex_unlock(&global_files_mutex);
|
||||
mutex_unlock(&global_error_collector.path_mutex);
|
||||
// mutex_unlock(&global_error_collector.path_mutex);
|
||||
return path;
|
||||
}
|
||||
|
||||
gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
|
||||
GB_ASSERT(index >= 0);
|
||||
mutex_lock(&global_error_collector.path_mutex);
|
||||
// mutex_lock(&global_error_collector.path_mutex);
|
||||
mutex_lock(&global_files_mutex);
|
||||
|
||||
AstFile *file = nullptr;
|
||||
@@ -148,7 +148,18 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
|
||||
}
|
||||
|
||||
mutex_unlock(&global_files_mutex);
|
||||
mutex_unlock(&global_error_collector.path_mutex);
|
||||
// mutex_unlock(&global_error_collector.path_mutex);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
// use AFTER PARSER
|
||||
gb_internal AstFile *thread_unsafe_get_ast_file_from_id(i32 index) {
|
||||
GB_ASSERT(index >= 0);
|
||||
AstFile *file = nullptr;
|
||||
if (index < global_files.count) {
|
||||
file = global_files[index];
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
+13
-11
@@ -714,13 +714,15 @@ extern "C++" {
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(DISABLE_ASSERT)
|
||||
#define GB_ASSERT(cond) gb_unused(cond)
|
||||
#endif
|
||||
|
||||
#ifndef GB_ASSERT
|
||||
#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL)
|
||||
#endif
|
||||
|
||||
#ifndef GB_ASSERT_NOT_NULL
|
||||
#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL")
|
||||
#endif
|
||||
|
||||
// NOTE(bill): Things that shouldn't happen with a message!
|
||||
#ifndef GB_PANIC
|
||||
@@ -3719,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) {
|
||||
}
|
||||
|
||||
gb_inline char *gb_strcpy(char *dest, char const *source) {
|
||||
GB_ASSERT_NOT_NULL(dest);
|
||||
GB_ASSERT(dest != NULL);
|
||||
if (source) {
|
||||
char *str = dest;
|
||||
while (*source) *str++ = *source++;
|
||||
@@ -3729,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) {
|
||||
|
||||
|
||||
gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
|
||||
GB_ASSERT_NOT_NULL(dest);
|
||||
GB_ASSERT(dest != NULL);
|
||||
if (source) {
|
||||
char *str = dest;
|
||||
while (len > 0 && *source) {
|
||||
@@ -3746,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
|
||||
|
||||
gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) {
|
||||
isize result = 0;
|
||||
GB_ASSERT_NOT_NULL(dest);
|
||||
GB_ASSERT(dest != NULL);
|
||||
if (source) {
|
||||
char const *source_start = source;
|
||||
char *str = dest;
|
||||
@@ -5636,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
|
||||
|
||||
void gb_file_free_contents(gbFileContents *fc) {
|
||||
if (fc == NULL || fc->size == 0) return;
|
||||
GB_ASSERT_NOT_NULL(fc->data);
|
||||
GB_ASSERT(fc->data != NULL);
|
||||
gb_free(fc->allocator, fc->data);
|
||||
fc->data = NULL;
|
||||
fc->size = 0;
|
||||
@@ -5648,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) {
|
||||
|
||||
gb_inline b32 gb_path_is_absolute(char const *path) {
|
||||
b32 result = false;
|
||||
GB_ASSERT_NOT_NULL(path);
|
||||
GB_ASSERT(path != NULL);
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
result == (gb_strlen(path) > 2) &&
|
||||
gb_char_is_alpha(path[0]) &&
|
||||
@@ -5663,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut
|
||||
|
||||
gb_inline b32 gb_path_is_root(char const *path) {
|
||||
b32 result = false;
|
||||
GB_ASSERT_NOT_NULL(path);
|
||||
GB_ASSERT(path != NULL);
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
result = gb_path_is_absolute(path) && (gb_strlen(path) == 3);
|
||||
#else
|
||||
@@ -5674,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) {
|
||||
|
||||
gb_inline char const *gb_path_base_name(char const *path) {
|
||||
char const *ls;
|
||||
GB_ASSERT_NOT_NULL(path);
|
||||
GB_ASSERT(path != NULL);
|
||||
ls = gb_char_last_occurence(path, '/');
|
||||
return (ls == NULL) ? path : ls+1;
|
||||
}
|
||||
|
||||
gb_inline char const *gb_path_extension(char const *path) {
|
||||
char const *ld;
|
||||
GB_ASSERT_NOT_NULL(path);
|
||||
GB_ASSERT(path != NULL);
|
||||
ld = gb_char_last_occurence(path, '.');
|
||||
return (ld == NULL) ? NULL : ld+1;
|
||||
}
|
||||
|
||||
+27
-8
@@ -161,21 +161,32 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
try_cross_linking:;
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String section_name = str_lit("msvc-link");
|
||||
bool is_windows = build_context.metrics.os == TargetOs_windows;
|
||||
#else
|
||||
String section_name = str_lit("lld-link");
|
||||
bool is_windows = false;
|
||||
#endif
|
||||
|
||||
bool is_osx = build_context.metrics.os == TargetOs_darwin;
|
||||
|
||||
|
||||
switch (build_context.linker_choice) {
|
||||
case Linker_Default: break;
|
||||
case Linker_lld: section_name = str_lit("lld-link"); break;
|
||||
#if defined(GB_SYSTEM_LINUX)
|
||||
case Linker_mold: section_name = str_lit("mold-link"); break;
|
||||
#endif
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
case Linker_radlink: section_name = str_lit("rad-link"); break;
|
||||
#endif
|
||||
default:
|
||||
gb_printf_err("'%.*s' linker is not support for this platform\n", LIT(linker_choices[build_context.linker_choice]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (is_windows) {
|
||||
String section_name = str_lit("msvc-link");
|
||||
switch (build_context.linker_choice) {
|
||||
case Linker_Default: break;
|
||||
case Linker_lld: section_name = str_lit("lld-link"); break;
|
||||
case Linker_radlink: section_name = str_lit("rad-link"); break;
|
||||
}
|
||||
timings_start_section(timings, section_name);
|
||||
|
||||
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||
@@ -281,7 +292,11 @@ try_cross_linking:;
|
||||
link_settings = gb_string_append_fmt(link_settings, " /NOENTRY");
|
||||
}
|
||||
} else {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
||||
// For i386 with CRT, libcmt provides the entry point
|
||||
// For other cases or no_crt, we need to specify the entry point
|
||||
if (!(build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt)) {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.build_paths[BuildPath_Symbols].name != "") {
|
||||
@@ -419,7 +434,8 @@ try_cross_linking:;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timings_start_section(timings, str_lit("ld-link"));
|
||||
|
||||
timings_start_section(timings, section_name);
|
||||
|
||||
int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
|
||||
|
||||
@@ -952,6 +968,9 @@ try_cross_linking:;
|
||||
if (build_context.linker_choice == Linker_lld) {
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
|
||||
result = system_exec_command_line_app("lld-link", link_command_line);
|
||||
} else if (build_context.linker_choice == Linker_mold) {
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=mold");
|
||||
result = system_exec_command_line_app("mold-link", link_command_line);
|
||||
} else {
|
||||
result = system_exec_command_line_app("ld-link", link_command_line);
|
||||
}
|
||||
|
||||
+17
-17
@@ -522,6 +522,23 @@ namespace lbAbiAmd64Win64 {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
|
||||
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
if (LLVMCountStructElementTypes(type) != 2) {
|
||||
return false;
|
||||
}
|
||||
LLVMTypeRef fields[2] = {};
|
||||
LLVMGetStructElementTypes(type, fields);
|
||||
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
|
||||
|
||||
}
|
||||
|
||||
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
|
||||
namespace lbAbiAmd64SysV {
|
||||
enum RegClass {
|
||||
@@ -652,23 +669,6 @@ namespace lbAbiAmd64SysV {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
|
||||
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
if (LLVMCountStructElementTypes(type) != 2) {
|
||||
return false;
|
||||
}
|
||||
LLVMTypeRef fields[2] = {};
|
||||
LLVMGetStructElementTypes(type, fields);
|
||||
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_aggregate(LLVMTypeRef type) {
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||
switch (kind) {
|
||||
|
||||
+311
-223
@@ -8,7 +8,11 @@
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_IGNORE_VERIFICATION
|
||||
#define LLVM_IGNORE_VERIFICATION 0
|
||||
#define LLVM_IGNORE_VERIFICATION build_context.internal_ignore_llvm_verification
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_WEAK_MONOMORPHIZATION
|
||||
#define LLVM_WEAK_MONOMORPHIZATION (USE_SEPARATE_MODULES && build_context.internal_weak_monomorphization)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -242,26 +246,12 @@ gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type
|
||||
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));
|
||||
gb_internal void lb_equal_proc_generate_body(lbModule *m, lbProcedure *p) {
|
||||
Type *type = p->internal_gen_type;
|
||||
|
||||
Type *pt = alloc_type_pointer(type);
|
||||
LLVMTypeRef ptr_type = lb_type(m, pt);
|
||||
|
||||
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;
|
||||
GB_ASSERT(compare_proc != nullptr);
|
||||
return {compare_proc->value, compare_proc->type};
|
||||
}
|
||||
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
@@ -389,9 +379,29 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
}
|
||||
|
||||
compare_proc = p;
|
||||
return {compare_proc->value, compare_proc->type};
|
||||
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(is_type_comparable(type));
|
||||
|
||||
String proc_name = lb_internal_gen_name_from_type("__$equal", type);
|
||||
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
|
||||
if (found) {
|
||||
lbProcedure *p = *found;
|
||||
GB_ASSERT(p != nullptr);
|
||||
return {p->value, p->type};
|
||||
}
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
p->internal_gen_type = type;
|
||||
p->generate_body = lb_equal_proc_generate_body;
|
||||
|
||||
// p->generate_body(m, p);
|
||||
mpsc_enqueue(&m->procedures_to_generate, p);
|
||||
|
||||
return {p->value, p->type};
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
|
||||
@@ -620,6 +630,7 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
#define LLVM_SET_VALUE_NAME(value, name) LLVMSetValueName2((value), (name), gb_count_of((name))-1);
|
||||
|
||||
|
||||
gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(!build_context.dynamic_map_calls);
|
||||
type = base_type(type);
|
||||
@@ -634,6 +645,9 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
|
||||
string_map_set(&m->gen_procs, proc_name, p);
|
||||
|
||||
p->internal_gen_type = type;
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
@@ -1153,15 +1167,6 @@ gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_pt
|
||||
return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
|
||||
}
|
||||
|
||||
|
||||
struct lbGlobalVariable {
|
||||
lbValue var;
|
||||
lbValue init;
|
||||
DeclInfo *decl;
|
||||
bool is_initialized;
|
||||
};
|
||||
|
||||
|
||||
gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
|
||||
if (build_context.metrics.os != TargetOs_darwin) {
|
||||
return nullptr;
|
||||
@@ -1900,12 +1905,16 @@ gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=fa
|
||||
}
|
||||
|
||||
gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
|
||||
if (LLVM_IGNORE_VERIFICATION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *llvm_error = nullptr;
|
||||
defer (LLVMDisposeMessage(llvm_error));
|
||||
lbModule *m = cast(lbModule *)data;
|
||||
|
||||
if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
|
||||
gb_printf_err("LLVM Error:\n%s\n", llvm_error);
|
||||
gb_printf_err("LLVM Error in module %s:\n%s\n", m->module_name, llvm_error);
|
||||
if (build_context.keep_temp_files) {
|
||||
TIME_SECTION("LLVM Print Module to File");
|
||||
String filepath_ll = lb_filepath_ll_for_module(m);
|
||||
@@ -1921,6 +1930,132 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast *init_expr, lbGlobalVariable &var) {
|
||||
if (init_expr != nullptr) {
|
||||
lbValue init = lb_build_expr(p, init_expr);
|
||||
if (init.value == nullptr) {
|
||||
LLVMTypeRef global_type = llvm_addr_type(p->module, var.var);
|
||||
if (is_type_untyped_nil(init.type)) {
|
||||
LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
|
||||
}
|
||||
|
||||
if (is_type_any(e->type)) {
|
||||
var.init = init;
|
||||
} else if (lb_is_const_or_global(init)) {
|
||||
if (!var.is_initialized) {
|
||||
if (is_type_proc(init.type)) {
|
||||
init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type));
|
||||
}
|
||||
LLVMSetInitializer(var.var.value, init.value);
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
var.init = init;
|
||||
}
|
||||
}
|
||||
|
||||
if (var.init.value != nullptr) {
|
||||
GB_ASSERT(!var.is_initialized);
|
||||
Type *t = type_deref(var.var.type);
|
||||
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for 'any' type
|
||||
Type *var_type = default_type(var.init.type);
|
||||
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(m, var_type, {}, make_string_c(var_name));
|
||||
lb_addr_store(p, g, var.init);
|
||||
lbValue gp = lb_addr_get_ptr(p, g);
|
||||
|
||||
lbValue data = lb_emit_struct_ep(p, var.var, 0);
|
||||
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
|
||||
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
|
||||
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
|
||||
} else {
|
||||
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
|
||||
lbValue src0 = lb_emit_conv(p, var.init, t);
|
||||
LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
|
||||
LLVMValueRef dst = var.var.value;
|
||||
LLVMBuildStore(p->builder, src, dst);
|
||||
}
|
||||
|
||||
var.is_initialized = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedure *p) {
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
lb_setup_type_info_data(m);
|
||||
|
||||
if (p->objc_names) {
|
||||
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(m, p->objc_names->type), p->objc_names->value, nullptr, 0, "");
|
||||
}
|
||||
Type *dummy_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
|
||||
LLVMTypeRef raw_dummy_type = lb_type_internal_for_procedures_raw(m, dummy_type);
|
||||
|
||||
for (auto &var : *p->global_variables) {
|
||||
if (var.is_initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lbModule *entity_module = m;
|
||||
|
||||
Entity *e = var.decl->entity;
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
e->code_gen_module = entity_module;
|
||||
Ast *init_expr = var.decl->init_expr;
|
||||
|
||||
if (init_expr == nullptr && var.init.value == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type_size_of(e->type) > 8) {
|
||||
String ename = lb_get_entity_name(m, e);
|
||||
gbString name = gb_string_make(permanent_allocator(), "");
|
||||
name = gb_string_appendc(name, "__$startup$");
|
||||
name = gb_string_append_length(name, ename.text, ename.len);
|
||||
|
||||
lbProcedure *dummy = lb_create_dummy_procedure(m, make_string_c(name), dummy_type);
|
||||
LLVMSetVisibility(dummy->value, LLVMHiddenVisibility);
|
||||
LLVMSetLinkage(dummy->value, LLVMWeakAnyLinkage);
|
||||
|
||||
lb_begin_procedure_body(dummy);
|
||||
lb_init_global_var(m, dummy, e, init_expr, var);
|
||||
lb_end_procedure_body(dummy);
|
||||
|
||||
LLVMValueRef context_ptr = lb_find_or_generate_context_ptr(p).addr.value;
|
||||
LLVMBuildCall2(p->builder, raw_dummy_type, dummy->value, &context_ptr, 1, "");
|
||||
} else {
|
||||
lb_init_global_var(m, p, e, init_expr, var);
|
||||
}
|
||||
}
|
||||
CheckerInfo *info = m->gen->info;
|
||||
|
||||
for (Entity *e : info->init_procedures) {
|
||||
lbValue value = lb_find_procedure_value_from_entity(m, e);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none);
|
||||
}
|
||||
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
|
||||
@@ -1935,104 +2070,11 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
|
||||
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
p->global_variables = &global_variables;
|
||||
p->objc_names = objc_names;
|
||||
|
||||
lb_setup_type_info_data(main_module);
|
||||
lb_create_startup_runtime_generate_body(main_module, p);
|
||||
|
||||
if (objc_names) {
|
||||
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
|
||||
}
|
||||
|
||||
for (auto &var : global_variables) {
|
||||
if (var.is_initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lbModule *entity_module = main_module;
|
||||
|
||||
Entity *e = var.decl->entity;
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
e->code_gen_module = entity_module;
|
||||
|
||||
Ast *init_expr = var.decl->init_expr;
|
||||
if (init_expr != nullptr) {
|
||||
lbValue init = lb_build_expr(p, init_expr);
|
||||
if (init.value == nullptr) {
|
||||
LLVMTypeRef global_type = llvm_addr_type(p->module, var.var);
|
||||
if (is_type_untyped_nil(init.type)) {
|
||||
LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
|
||||
}
|
||||
|
||||
if (is_type_any(e->type) || is_type_union(e->type)) {
|
||||
var.init = init;
|
||||
} else if (lb_is_const_or_global(init)) {
|
||||
if (!var.is_initialized) {
|
||||
if (is_type_proc(init.type)) {
|
||||
init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type));
|
||||
}
|
||||
LLVMSetInitializer(var.var.value, init.value);
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
var.init = init;
|
||||
}
|
||||
}
|
||||
|
||||
if (var.init.value != nullptr) {
|
||||
GB_ASSERT(!var.is_initialized);
|
||||
Type *t = type_deref(var.var.type);
|
||||
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for 'any' type
|
||||
Type *var_type = default_type(var.init.type);
|
||||
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, {}, make_string_c(var_name));
|
||||
lb_addr_store(p, g, var.init);
|
||||
lbValue gp = lb_addr_get_ptr(p, g);
|
||||
|
||||
lbValue data = lb_emit_struct_ep(p, var.var, 0);
|
||||
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
|
||||
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
|
||||
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
|
||||
} else {
|
||||
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
|
||||
lbValue src0 = lb_emit_conv(p, var.init, t);
|
||||
LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
|
||||
LLVMValueRef dst = var.var.value;
|
||||
LLVMBuildStore(p->builder, src, dst);
|
||||
}
|
||||
|
||||
var.is_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
CheckerInfo *info = main_module->gen->info;
|
||||
|
||||
for (Entity *e : info->init_procedures) {
|
||||
lbValue value = lb_find_procedure_value_from_entity(main_module, e);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none);
|
||||
}
|
||||
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
|
||||
lb_verify_function(main_module, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -2073,7 +2115,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
|
||||
|
||||
for (Entity *e : m->global_procedures_to_create) {
|
||||
(void)lb_get_entity_name(m, e);
|
||||
array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
|
||||
mpsc_enqueue(&m->procedures_to_generate, lb_create_procedure(m, e));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2097,8 +2139,6 @@ gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) {
|
||||
}
|
||||
|
||||
gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) {
|
||||
auto *min_dep_set = &info->minimum_dependency_set;
|
||||
|
||||
for (Entity *e : info->entities) {
|
||||
String name = e->token.string;
|
||||
Scope * scope = e->scope;
|
||||
@@ -2135,14 +2175,19 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
|
||||
}
|
||||
}
|
||||
|
||||
if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
|
||||
if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
|
||||
// NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
|
||||
// // NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
// continue;
|
||||
// }
|
||||
|
||||
lbModule *m = &gen->default_module;
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
m = lb_module_of_entity(gen, e);
|
||||
m = lb_module_of_entity(gen, e, m);
|
||||
}
|
||||
GB_ASSERT(m != nullptr);
|
||||
|
||||
@@ -2258,7 +2303,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
|
||||
}
|
||||
|
||||
for (lbProcedure *p : m->procedures_to_generate) {
|
||||
MUTEX_GUARD_BLOCK(&m->generated_procedures_mutex) for (lbProcedure *p : m->generated_procedures) {
|
||||
if (p->body != nullptr) { // Build Procedure
|
||||
lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default;
|
||||
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
|
||||
@@ -2297,17 +2342,23 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
}
|
||||
|
||||
|
||||
void lb_remove_unused_functions_and_globals(lbGenerator *gen) {
|
||||
for (auto &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
lb_run_remove_unused_function_pass(m);
|
||||
lb_run_remove_unused_globals_pass(m);
|
||||
}
|
||||
}
|
||||
|
||||
struct lbLLVMModulePassWorkerData {
|
||||
lbModule *m;
|
||||
LLVMTargetMachineRef target_machine;
|
||||
bool do_threading;
|
||||
};
|
||||
|
||||
gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
||||
auto wd = cast(lbLLVMModulePassWorkerData *)data;
|
||||
|
||||
lb_run_remove_unused_function_pass(wd->m);
|
||||
lb_run_remove_unused_globals_pass(wd->m);
|
||||
|
||||
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
|
||||
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
|
||||
LLVMRunPassManager(module_pass_manager, wd->m->mod);
|
||||
@@ -2383,6 +2434,17 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (LLVM_IGNORE_VERIFICATION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wd->do_threading) {
|
||||
thread_pool_add_task(lb_llvm_module_verification_worker_proc, wd->m);
|
||||
} else {
|
||||
lb_llvm_module_verification_worker_proc(wd->m);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2390,8 +2452,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
||||
|
||||
gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
|
||||
lbModule *m = cast(lbModule *)data;
|
||||
for (isize i = 0; i < m->procedures_to_generate.count; i++) {
|
||||
lbProcedure *p = m->procedures_to_generate[i];
|
||||
for (lbProcedure *p = nullptr; mpsc_dequeue(&m->procedures_to_generate, &p); /**/) {
|
||||
lb_generate_procedure(p->module, p);
|
||||
}
|
||||
return 0;
|
||||
@@ -2415,10 +2476,15 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
|
||||
|
||||
gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
|
||||
lbModule *m = cast(lbModule *)data;
|
||||
for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
|
||||
lbProcedure *p = m->missing_procedures_to_check[i];
|
||||
debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
|
||||
lb_generate_procedure(m, p);
|
||||
for (lbProcedure *p = nullptr; mpsc_dequeue(&m->missing_procedures_to_check, &p); /**/) {
|
||||
if (!p->is_done.load(std::memory_order_relaxed)) {
|
||||
debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
|
||||
lb_generate_procedure(m, p);
|
||||
}
|
||||
|
||||
for (lbProcedure *nested = nullptr; mpsc_dequeue(&m->procedures_to_generate, &nested); /**/) {
|
||||
mpsc_enqueue(&m->missing_procedures_to_check, nested);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2438,6 +2504,12 @@ gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_thread
|
||||
lb_generate_missing_procedures_to_check_worker_proc(m);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
GB_ASSERT(m->missing_procedures_to_check.count == 0);
|
||||
GB_ASSERT(m->procedures_to_generate.count == 0);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
|
||||
@@ -2465,19 +2537,16 @@ gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
|
||||
gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool do_threading) {
|
||||
if (do_threading) {
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
|
||||
wd->m = m;
|
||||
wd->target_machine = m->target_machine;
|
||||
wd->do_threading = true;
|
||||
|
||||
if (do_threading) {
|
||||
thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
|
||||
} else {
|
||||
lb_llvm_module_pass_worker_proc(wd);
|
||||
}
|
||||
thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
|
||||
}
|
||||
thread_pool_wait();
|
||||
} else {
|
||||
@@ -2486,6 +2555,7 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
|
||||
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
|
||||
wd->m = m;
|
||||
wd->target_machine = m->target_machine;
|
||||
wd->do_threading = false;
|
||||
lb_llvm_module_pass_worker_proc(wd);
|
||||
}
|
||||
}
|
||||
@@ -2566,31 +2636,6 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) {
|
||||
if (LLVM_IGNORE_VERIFICATION) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (do_threading) {
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
thread_pool_add_task(lb_llvm_module_verification_worker_proc, m);
|
||||
}
|
||||
thread_pool_wait();
|
||||
|
||||
} else {
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
if (lb_llvm_module_verification_worker_proc(m)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) {
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
@@ -2686,8 +2731,15 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
|
||||
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
|
||||
call_cleanup = false;
|
||||
} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
|
||||
} else if (build_context.metrics.os == TargetOs_windows && build_context.no_crt) {
|
||||
name = str_lit("mainCRTStartup");
|
||||
} else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt) {
|
||||
// Windows i386 with CRT: libcmt expects _main (main with underscore prefix)
|
||||
name = str_lit("main");
|
||||
has_args = true;
|
||||
slice_init(¶ms->Tuple.variables, permanent_allocator(), 2);
|
||||
params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
|
||||
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true);
|
||||
} else if (is_arch_wasm()) {
|
||||
name = str_lit("_start");
|
||||
call_cleanup = false;
|
||||
@@ -2813,16 +2865,19 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
}
|
||||
|
||||
gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
|
||||
if (p->is_done) {
|
||||
if (p->is_done.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->body != nullptr) { // Build Procedure
|
||||
m->curr_procedure = p;
|
||||
lb_begin_procedure_body(p);
|
||||
lb_build_stmt(p, p->body);
|
||||
lb_end_procedure_body(p);
|
||||
p->is_done = true;
|
||||
p->is_done.store(true, std::memory_order_relaxed);
|
||||
m->curr_procedure = nullptr;
|
||||
} else if (p->generate_body != nullptr) {
|
||||
p->generate_body(m, p);
|
||||
}
|
||||
|
||||
// Add Flags
|
||||
@@ -2831,6 +2886,9 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
|
||||
}
|
||||
|
||||
lb_verify_function(m, p, true);
|
||||
|
||||
MUTEX_GUARD(&m->generated_procedures_mutex);
|
||||
array_add(&m->generated_procedures, p);
|
||||
}
|
||||
|
||||
|
||||
@@ -2845,8 +2903,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
lbModule *default_module = &gen->default_module;
|
||||
CheckerInfo *info = gen->info;
|
||||
|
||||
auto *min_dep_set = &info->minimum_dependency_set;
|
||||
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_amd64:
|
||||
case TargetArch_i386:
|
||||
@@ -3162,6 +3218,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
String link_name = e->Procedure.link_name;
|
||||
if (e->pkg->kind == Package_Runtime) {
|
||||
if (link_name == "main" ||
|
||||
link_name == "_main" ||
|
||||
link_name == "DllMain" ||
|
||||
link_name == "WinMain" ||
|
||||
link_name == "wWinMain" ||
|
||||
@@ -3184,7 +3241,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ptr_set_exists(min_dep_set, e)) {
|
||||
if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3194,47 +3251,32 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
}
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
|
||||
lbModule *default_module = &gen->default_module;
|
||||
|
||||
lbModule *m = default_module;
|
||||
lbModule *e_module = lb_module_of_entity(gen, e, default_module);
|
||||
|
||||
bool const split_globals_across_modules = false;
|
||||
if (split_globals_across_modules) {
|
||||
m = e_module;
|
||||
}
|
||||
|
||||
lbModule *m = &gen->default_module;
|
||||
String name = lb_get_entity_name(m, e);
|
||||
|
||||
lbValue g = {};
|
||||
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
|
||||
g.type = alloc_type_pointer(e->type);
|
||||
|
||||
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
|
||||
|
||||
if (is_foreign) {
|
||||
LLVMSetLinkage(g.value, LLVMExternalLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
|
||||
LLVMSetExternallyInitialized(g.value, true);
|
||||
lb_add_foreign_library_path(m, e->Variable.foreign_library);
|
||||
} else {
|
||||
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
|
||||
}
|
||||
if (is_export) {
|
||||
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
|
||||
} else if (!is_foreign) {
|
||||
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
|
||||
}
|
||||
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
|
||||
LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
|
||||
|
||||
if (e->Variable.link_section.len > 0) {
|
||||
LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
|
||||
}
|
||||
|
||||
lbGlobalVariable var = {};
|
||||
var.var = g;
|
||||
var.decl = decl;
|
||||
|
||||
lbValue g = {};
|
||||
g.type = alloc_type_pointer(e->type);
|
||||
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
|
||||
|
||||
if (decl->init_expr != nullptr) {
|
||||
TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
|
||||
if (!is_type_any(e->type) && !is_type_union(e->type)) {
|
||||
if (!is_type_any(e->type)) {
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
auto cc = LB_CONST_CONTEXT_DEFAULT;
|
||||
@@ -3244,6 +3286,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
ExactValue v = tav.value;
|
||||
lbValue init = lb_const_value(m, tav.type, v, cc);
|
||||
|
||||
LLVMDeleteGlobal(g.value);
|
||||
g.value = nullptr;
|
||||
g.value = LLVMAddGlobal(m->mod, LLVMTypeOf(init.value), alloc_cstring(permanent_allocator(), name));
|
||||
|
||||
LLVMSetInitializer(g.value, init.value);
|
||||
var.is_initialized = true;
|
||||
if (cc.is_rodata) {
|
||||
@@ -3262,16 +3309,33 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMSetGlobalConstant(g.value, true);
|
||||
}
|
||||
|
||||
|
||||
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
|
||||
|
||||
if (is_foreign) {
|
||||
LLVMSetLinkage(g.value, LLVMExternalLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
|
||||
LLVMSetExternallyInitialized(g.value, true);
|
||||
lb_add_foreign_library_path(m, e->Variable.foreign_library);
|
||||
} else if (LLVMGetInitializer(g.value) == nullptr) {
|
||||
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
|
||||
}
|
||||
if (is_export) {
|
||||
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
|
||||
} else if (!is_foreign) {
|
||||
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
|
||||
}
|
||||
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
|
||||
LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
|
||||
|
||||
if (e->Variable.link_section.len > 0) {
|
||||
LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
|
||||
}
|
||||
if (e->flags & EntityFlag_Require) {
|
||||
lb_append_to_compiler_used(m, g.value);
|
||||
}
|
||||
|
||||
array_add(&global_variables, var);
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
lb_add_member(m, name, g);
|
||||
|
||||
|
||||
if (m->debug_builder) {
|
||||
String global_name = e->token.string;
|
||||
if (global_name.len != 0 && global_name != "_") {
|
||||
@@ -3300,6 +3364,27 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if (default_module == m) {
|
||||
g.value = LLVMConstPointerCast(g.value, lb_type(m, alloc_type_pointer(e->type)));
|
||||
|
||||
var.var = g;
|
||||
array_add(&global_variables, var);
|
||||
} else {
|
||||
lbValue local_g = {};
|
||||
local_g.type = alloc_type_pointer(e->type);
|
||||
local_g.value = LLVMAddGlobal(default_module->mod, lb_type(default_module, e->type), alloc_cstring(permanent_allocator(), name));
|
||||
LLVMSetLinkage(local_g.value, LLVMExternalLinkage);
|
||||
|
||||
var.var = local_g;
|
||||
array_add(&global_variables, var);
|
||||
|
||||
lb_add_entity(default_module, e, local_g);
|
||||
lb_add_member(default_module, name, local_g);
|
||||
}
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
lb_add_member(m, name, g);
|
||||
}
|
||||
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
@@ -3471,15 +3556,23 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("LLVM Add Foreign Library Paths");
|
||||
lb_add_foreign_library_paths(gen);
|
||||
|
||||
TIME_SECTION("LLVM Function Pass");
|
||||
lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG);
|
||||
|
||||
TIME_SECTION("LLVM Module Pass");
|
||||
lb_llvm_module_passes(gen, do_threading);
|
||||
TIME_SECTION("LLVM Remove Unused Functions and Globals");
|
||||
lb_remove_unused_functions_and_globals(gen);
|
||||
|
||||
TIME_SECTION("LLVM Module Verification");
|
||||
if (!lb_llvm_module_verification(gen, do_threading)) {
|
||||
return false;
|
||||
TIME_SECTION("LLVM Module Pass and Verification");
|
||||
lb_llvm_module_passes_and_verification(gen, do_threading);
|
||||
|
||||
TIME_SECTION("LLVM Correct Entity Linkage");
|
||||
lb_correct_entity_linkage(gen);
|
||||
|
||||
if (build_context.build_diagnostics) {
|
||||
lb_do_build_diagnostics(gen);
|
||||
}
|
||||
|
||||
llvm_error = nullptr;
|
||||
@@ -3508,11 +3601,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("LLVM Add Foreign Library Paths");
|
||||
lb_add_foreign_library_paths(gen);
|
||||
|
||||
TIME_SECTION("LLVM Correct Entity Linkage");
|
||||
lb_correct_entity_linkage(gen);
|
||||
|
||||
////////////////////////////////////////////
|
||||
for (auto const &entry: gen->modules) {
|
||||
|
||||
+30
-9
@@ -147,9 +147,13 @@ struct lbModule {
|
||||
LLVMModuleRef mod;
|
||||
LLVMContextRef ctx;
|
||||
|
||||
Checker *checker;
|
||||
|
||||
struct lbGenerator *gen;
|
||||
LLVMTargetMachineRef target_machine;
|
||||
|
||||
lbModule *polymorphic_module;
|
||||
|
||||
CheckerInfo *info;
|
||||
AstPackage *pkg; // possibly associated
|
||||
AstFile *file; // possibly associated
|
||||
@@ -171,7 +175,8 @@ struct lbModule {
|
||||
StringMap<lbValue> members;
|
||||
StringMap<lbProcedure *> procedures;
|
||||
PtrMap<LLVMValueRef, Entity *> procedure_values;
|
||||
Array<lbProcedure *> missing_procedures_to_check;
|
||||
|
||||
MPSCQueue<lbProcedure *> missing_procedures_to_check;
|
||||
|
||||
StringMap<LLVMValueRef> const_strings;
|
||||
String16Map<LLVMValueRef> const_string16s;
|
||||
@@ -180,10 +185,13 @@ struct lbModule {
|
||||
|
||||
StringMap<lbProcedure *> gen_procs; // key is the canonicalized name
|
||||
|
||||
Array<lbProcedure *> procedures_to_generate;
|
||||
MPSCQueue<lbProcedure *> procedures_to_generate;
|
||||
Array<Entity *> global_procedures_to_create;
|
||||
Array<Entity *> global_types_to_create;
|
||||
|
||||
BlockingMutex generated_procedures_mutex;
|
||||
Array<lbProcedure *> generated_procedures;
|
||||
|
||||
lbProcedure *curr_procedure;
|
||||
|
||||
LLVMBuilderRef const_dummy_builder;
|
||||
@@ -232,8 +240,7 @@ struct lbGenerator : LinkerData {
|
||||
PtrMap<LLVMContextRef, lbModule *> modules_through_ctx;
|
||||
lbModule default_module;
|
||||
|
||||
RecursiveMutex anonymous_proc_lits_mutex;
|
||||
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
|
||||
lbModule *equal_module;
|
||||
|
||||
isize used_module_count;
|
||||
|
||||
@@ -329,6 +336,14 @@ struct lbVariadicReuseSlices {
|
||||
lbAddr slice_addr;
|
||||
};
|
||||
|
||||
struct lbGlobalVariable {
|
||||
lbValue var;
|
||||
lbValue init;
|
||||
DeclInfo *decl;
|
||||
bool is_initialized;
|
||||
};
|
||||
|
||||
|
||||
struct lbProcedure {
|
||||
u32 flags;
|
||||
u16 state_flags;
|
||||
@@ -351,9 +366,9 @@ struct lbProcedure {
|
||||
|
||||
lbFunctionType *abi_function_type;
|
||||
|
||||
LLVMValueRef value;
|
||||
LLVMBuilderRef builder;
|
||||
bool is_done;
|
||||
LLVMValueRef value;
|
||||
LLVMBuilderRef builder;
|
||||
std::atomic<bool> is_done;
|
||||
|
||||
lbAddr return_ptr;
|
||||
Array<lbDefer> defer_stmts;
|
||||
@@ -391,6 +406,12 @@ struct lbProcedure {
|
||||
PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
|
||||
|
||||
Array<lbValue> asan_stack_locals;
|
||||
|
||||
void (*generate_body)(lbModule *m, lbProcedure *p);
|
||||
Array<lbGlobalVariable> *global_variables;
|
||||
lbProcedure *objc_names;
|
||||
|
||||
Type *internal_gen_type; // map_set, map_get, etc.
|
||||
};
|
||||
|
||||
|
||||
@@ -434,7 +455,7 @@ static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, {
|
||||
|
||||
gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
|
||||
gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT);
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT, Type *value_type=nullptr);
|
||||
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
|
||||
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
|
||||
|
||||
@@ -583,7 +604,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
|
||||
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
|
||||
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
|
||||
|
||||
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
|
||||
|
||||
+240
-63
@@ -81,7 +81,7 @@ gb_internal String lb_get_const_string(lbModule *m, lbValue value) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
|
||||
gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst, bool *failure_) {
|
||||
LLVMTypeRef src = LLVMTypeOf(val);
|
||||
if (src == dst) {
|
||||
return val;
|
||||
@@ -96,15 +96,12 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
|
||||
case LLVMPointerTypeKind:
|
||||
return LLVMConstPointerCast(val, dst);
|
||||
case LLVMStructTypeKind:
|
||||
// GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst));
|
||||
// NOTE(bill): It's not possible to do a bit cast on a struct, why was this code even here in the first place?
|
||||
// It seems mostly to exist to get around the "anonymous -> named" struct assignments
|
||||
// return LLVMConstBitCast(val, dst);
|
||||
if (LLVMTypeOf(val) != dst) {
|
||||
if (failure_) *failure_ = true;
|
||||
}
|
||||
return val;
|
||||
default:
|
||||
GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst));
|
||||
}
|
||||
|
||||
if (failure_) *failure_ = true;
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -129,13 +126,13 @@ gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMVa
|
||||
LLVMConstNull(lb_type(m, t_i32)),
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 3);
|
||||
return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
|
||||
} else {
|
||||
LLVMValueRef values[2] = {
|
||||
data,
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 2);
|
||||
return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +144,13 @@ gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVM
|
||||
LLVMConstNull(lb_type(m, t_i32)),
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 3);
|
||||
return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
|
||||
} else {
|
||||
LLVMValueRef values[2] = {
|
||||
data,
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 2);
|
||||
return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,10 +162,10 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
unsigned elem_count = LLVMCountStructElementTypes(struct_type);
|
||||
if (elem_count == value_count) {
|
||||
return llvm_const_named_struct_internal(struct_type, values, value_count_);
|
||||
return llvm_const_named_struct_internal(m, struct_type, values, value_count_);
|
||||
}
|
||||
Type *bt = base_type(t);
|
||||
GB_ASSERT(bt->kind == Type_Struct);
|
||||
GB_ASSERT(bt->kind == Type_Struct || bt->kind == Type_Union);
|
||||
|
||||
GB_ASSERT(value_count_ == bt->Struct.fields.count);
|
||||
|
||||
@@ -185,25 +182,40 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
|
||||
}
|
||||
}
|
||||
|
||||
return llvm_const_named_struct_internal(struct_type, values_with_padding, values_with_padding_count);
|
||||
return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
unsigned elem_count = LLVMCountStructElementTypes(t);
|
||||
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
|
||||
bool failure = false;
|
||||
for (unsigned i = 0; i < elem_count; i++) {
|
||||
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
|
||||
values[i] = llvm_const_cast(values[i], elem_type);
|
||||
values[i] = llvm_const_cast(values[i], elem_type, &failure);
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
return LLVMConstStructInContext(m->ctx, values, value_count, true);
|
||||
}
|
||||
return LLVMConstNamedStruct(t, values, value_count);
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
|
||||
gb_internal LLVMValueRef llvm_const_array(lbModule *m, LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
bool failure = false;
|
||||
for (unsigned i = 0; i < value_count; i++) {
|
||||
values[i] = llvm_const_cast(values[i], elem_type);
|
||||
values[i] = llvm_const_cast(values[i], elem_type, &failure);
|
||||
}
|
||||
if (failure) {
|
||||
return LLVMConstStructInContext(m->ctx, values, value_count, false);
|
||||
}
|
||||
for (unsigned i = 0; i < value_count; i++) {
|
||||
if (elem_type != LLVMTypeOf(values[i])) {
|
||||
return LLVMConstStructInContext(m->ctx, values, value_count, false);
|
||||
}
|
||||
}
|
||||
|
||||
return LLVMConstArray(elem_type, values, value_count);
|
||||
}
|
||||
|
||||
@@ -461,7 +473,7 @@ gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type,
|
||||
return lb_addr_load(p, v).value;
|
||||
}
|
||||
|
||||
return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count);
|
||||
return llvm_const_array(m, lb_type(m, elem_type), values, cast(unsigned int)count);
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
|
||||
@@ -531,13 +543,13 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
|
||||
}
|
||||
|
||||
|
||||
if (is_type_raw_union(ft) || is_type_typeid(ft)) {
|
||||
if (is_type_raw_union(ft)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_elem_const(elem, ft);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) {
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc, Type *value_type) {
|
||||
if (cc.allow_local) {
|
||||
cc.is_rodata = false;
|
||||
}
|
||||
@@ -552,12 +564,101 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
type = core_type(type);
|
||||
value = convert_exact_value_for_type(value, type);
|
||||
|
||||
if (value.kind == ExactValue_Typeid) {
|
||||
return lb_typeid(m, value.value_typeid);
|
||||
}
|
||||
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
|
||||
|
||||
if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
if (is_type_union(type) && is_type_union_constantable(type)) {
|
||||
Type *bt = base_type(type);
|
||||
GB_ASSERT(bt->kind == Type_Union);
|
||||
if (bt->Union.variants.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
} else if (bt->Union.variants.count == 1) {
|
||||
Type *t = bt->Union.variants[0];
|
||||
lbValue cv = lb_const_value(m, t, value, cc);
|
||||
GB_ASSERT(LLVMIsConstant(cv.value));
|
||||
|
||||
LLVMTypeRef llvm_type = lb_type(m, original_type);
|
||||
|
||||
if (is_type_union_maybe_pointer(type)) {
|
||||
LLVMValueRef values[1] = {cv.value};
|
||||
res.value = llvm_const_named_struct_internal(m, llvm_type, values, 1);
|
||||
res.type = original_type;
|
||||
return res;
|
||||
} else {
|
||||
|
||||
unsigned tag_value = 1;
|
||||
if (bt->Union.kind == UnionType_no_nil) {
|
||||
tag_value = 0;
|
||||
}
|
||||
LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);
|
||||
LLVMValueRef padding = nullptr;
|
||||
LLVMValueRef values[3] = {cv.value, tag, padding};
|
||||
|
||||
isize value_count = 2;
|
||||
if (LLVMCountStructElementTypes(llvm_type) > 2) {
|
||||
value_count = 3;
|
||||
padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
|
||||
}
|
||||
res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count);
|
||||
res.type = original_type;
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
if (value_type == nullptr) {
|
||||
if (value.kind == ExactValue_Compound) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
} else if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value));
|
||||
|
||||
i64 block_size = bt->Union.variant_block_size;
|
||||
|
||||
if (are_types_identical(value_type, original_type)) {
|
||||
if (value.kind == ExactValue_Compound) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
} else if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type));
|
||||
}
|
||||
|
||||
lbValue cv = lb_const_value(m, value_type, value, cc, value_type);
|
||||
Type *variant_type = cv.type;
|
||||
|
||||
LLVMValueRef values[4] = {};
|
||||
unsigned value_count = 0;
|
||||
|
||||
values[value_count++] = cv.value;
|
||||
if (type_size_of(variant_type) != block_size) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
|
||||
values[value_count++] = LLVMConstNull(padding_type);
|
||||
}
|
||||
|
||||
Type *tag_type = union_tag_type(bt);
|
||||
LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
|
||||
i64 tag_index = union_variant_index(bt, variant_type);
|
||||
GB_ASSERT(tag_index >= 0);
|
||||
values[value_count++] = LLVMConstInt(llvm_tag_type, tag_index, false);
|
||||
i64 used_size = block_size + type_size_of(tag_type);
|
||||
i64 union_size = type_size_of(bt);
|
||||
i64 padding = union_size - used_size;
|
||||
if (padding > 0) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, 1);
|
||||
values[value_count++] = LLVMConstNull(padding_type);
|
||||
}
|
||||
|
||||
res.value = LLVMConstStructInContext(m->ctx, values, value_count, true);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (value.kind == ExactValue_Procedure) {
|
||||
@@ -583,7 +684,23 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
|
||||
// NOTE(bill): This has to be done AFTER the union stuff
|
||||
if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
|
||||
if (value.kind == ExactValue_Typeid) {
|
||||
return lb_typeid(m, value.value_typeid);
|
||||
}
|
||||
|
||||
if (value.kind == ExactValue_Compound) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
|
||||
|
||||
@@ -596,7 +713,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
GB_ASSERT(is_type_slice(type));
|
||||
res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value;
|
||||
return res;
|
||||
}else {
|
||||
} else {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
isize count = cl->elems.count;
|
||||
@@ -606,19 +723,39 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
count = gb_max(cast(isize)cl->max_count, count);
|
||||
Type *elem = base_type(type)->Slice.elem;
|
||||
Type *t = alloc_type_array(elem, count);
|
||||
lbValue backing_array = lb_const_value(m, t, value, cc);
|
||||
lbValue backing_array = lb_const_value(m, t, value, cc, nullptr);
|
||||
|
||||
LLVMValueRef array_data = nullptr;
|
||||
|
||||
|
||||
if (is_local) {
|
||||
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
|
||||
// its backing data on the stack
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
LLVMTypeRef llvm_type = lb_type(m, t);
|
||||
|
||||
array_data = llvm_alloca(p, llvm_type, 16);
|
||||
unsigned alignment = cast(unsigned)gb_max(type_align_of(t), 16);
|
||||
|
||||
|
||||
bool do_local_copy = false;
|
||||
if (do_local_copy) {
|
||||
array_data = llvm_alloca(p, llvm_type, alignment);
|
||||
|
||||
LLVMValueRef local_copy = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
|
||||
LLVMBuildStore(p->builder, backing_array.value, local_copy);
|
||||
|
||||
LLVMBuildMemCpy(p->builder,
|
||||
array_data, alignment,
|
||||
local_copy, alignment,
|
||||
LLVMConstInt(lb_type(m, t_int), type_size_of(t), false)
|
||||
);
|
||||
} else {
|
||||
array_data = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
|
||||
LLVMBuildStore(p->builder, backing_array.value, array_data);
|
||||
|
||||
array_data = LLVMBuildPointerCast(p->builder, array_data, LLVMPointerType(llvm_type, 0), "");
|
||||
}
|
||||
|
||||
LLVMBuildStore(p->builder, backing_array.value, array_data);
|
||||
|
||||
{
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
@@ -640,7 +777,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
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);
|
||||
array_data = LLVMAddGlobal(m->mod, LLVMTypeOf(backing_array.value), str);
|
||||
LLVMSetInitializer(array_data, backing_array.value);
|
||||
|
||||
if (cc.link_section.len > 0) {
|
||||
@@ -651,15 +788,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
|
||||
lbValue g = {};
|
||||
g.value = array_data;
|
||||
g.value = LLVMConstPointerCast(array_data, LLVMPointerType(lb_type(m, t), 0));
|
||||
g.type = t;
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
lb_add_member(m, name, g);
|
||||
|
||||
{
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef ptr = LLVMConstInBoundsGEP2(lb_type(m, t), array_data, indices, 2);
|
||||
LLVMValueRef ptr = g.value;
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
|
||||
LLVMValueRef values[2] = {ptr, len};
|
||||
|
||||
@@ -692,7 +828,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
GB_ASSERT(offset == s.len);
|
||||
|
||||
res.value = llvm_const_array(et, elems, cast(unsigned)count);
|
||||
res.value = llvm_const_array(m, et, elems, cast(unsigned)count);
|
||||
return res;
|
||||
}
|
||||
// NOTE(bill, 2021-10-07): Allow for array programming value constants
|
||||
@@ -722,7 +858,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
elems[i] = single_elem.value;
|
||||
}
|
||||
|
||||
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
|
||||
res.value = llvm_const_array(m, lb_type(m, elem), elems, cast(unsigned)count);
|
||||
return res;
|
||||
} else if (is_type_matrix(type) &&
|
||||
value.kind != ExactValue_Invalid &&
|
||||
@@ -734,7 +870,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
Type *elem = type->Matrix.elem;
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, cc);
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
|
||||
|
||||
i64 total_elem_count = matrix_type_total_internal_elems(type);
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)total_elem_count);
|
||||
@@ -756,7 +892,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
Type *elem = type->SimdVector.elem;
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, cc);
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
|
||||
for (i64 i = 0; i < count; i++) {
|
||||
@@ -981,7 +1117,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
aos_values[value_index++] = val;
|
||||
}
|
||||
@@ -996,7 +1132,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
aos_values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -1049,7 +1185,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
aos_values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
aos_values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->Struct.soa_count; i++) {
|
||||
aos_values[i] = nullptr;
|
||||
@@ -1116,7 +1252,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -1131,7 +1267,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -1146,12 +1282,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
|
||||
return res;
|
||||
} else if (value.value_compound->tav.type == elem_type) {
|
||||
} else if (are_types_identical(value.value_compound->tav.type, elem_type)) {
|
||||
// Compound is of array item type; expand its value to all items in array.
|
||||
LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
|
||||
|
||||
for (isize i = 0; i < type->Array.count; i++) {
|
||||
values[i] = lb_const_value(m, elem_type, value, cc).value;
|
||||
values[i] = lb_const_value(m, elem_type, value, cc, elem_type).value;
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
|
||||
@@ -1165,7 +1301,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->Array.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
@@ -1211,7 +1347,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -1226,7 +1362,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -1249,7 +1385,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
@@ -1294,7 +1430,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -1309,7 +1445,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -1328,7 +1464,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
}
|
||||
LLVMTypeRef et = lb_type(m, elem_type);
|
||||
|
||||
@@ -1336,7 +1472,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
values[i] = LLVMConstNull(et);
|
||||
}
|
||||
for (isize i = 0; i < total_elem_count; i++) {
|
||||
values[i] = llvm_const_cast(values[i], et);
|
||||
values[i] = llvm_const_cast(values[i], et, /*failure_*/nullptr);
|
||||
}
|
||||
|
||||
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
|
||||
@@ -1350,6 +1486,39 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
|
||||
if (is_type_raw_union(type)) {
|
||||
if (is_type_raw_union_constantable(type)) {
|
||||
GB_ASSERT(cl->elems.count == 1);
|
||||
GB_ASSERT(cl->elems[0]->kind == Ast_FieldValue);
|
||||
ast_node(fv, FieldValue, cl->elems[0]);
|
||||
Entity *f = entity_of_node(fv->field);
|
||||
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
lbValue value = lb_const_value(m, f->type, tav.value, cc, f->type);
|
||||
|
||||
LLVMValueRef values[2];
|
||||
unsigned value_count = 0;
|
||||
|
||||
values[value_count++] = value.value;
|
||||
|
||||
i64 union_alignment = type_align_of(type);
|
||||
i64 value_alignment = type_align_of(f->type);
|
||||
i64 alignment = gb_max(gb_min(value_alignment, union_alignment), 1);
|
||||
|
||||
i64 union_size = type_size_of(type);
|
||||
i64 value_size = lb_sizeof(LLVMTypeOf(value.value));
|
||||
i64 padding = union_size-value_size;
|
||||
if (padding > 0) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, alignment);
|
||||
values[value_count++] = LLVMConstNull(padding_type);
|
||||
}
|
||||
|
||||
LLVMValueRef res = LLVMConstStructInContext(m->ctx, values, value_count, /*packed*/padding > 0);
|
||||
|
||||
return {res, original_type};
|
||||
}
|
||||
|
||||
}
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
@@ -1377,7 +1546,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
if (sel.index.count == 1) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, cc).value;
|
||||
lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
|
||||
LLVMTypeRef value_type = LLVMTypeOf(value.value);
|
||||
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
|
||||
values[index] = value.value;
|
||||
visited[index] = true;
|
||||
} else {
|
||||
if (!visited[index]) {
|
||||
@@ -1423,7 +1595,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc).value;
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc, tav.type).value;
|
||||
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
|
||||
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
|
||||
} else if (is_local) {
|
||||
@@ -1477,7 +1649,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, val, cc).value;
|
||||
lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
|
||||
LLVMTypeRef value_type = LLVMTypeOf(value.value);
|
||||
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
|
||||
values[index] = value.value;
|
||||
visited[index] = true;
|
||||
}
|
||||
}
|
||||
@@ -1503,7 +1678,9 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
}
|
||||
|
||||
if (is_constant) {
|
||||
res.value = llvm_const_named_struct_internal(struct_type, values, cast(unsigned)value_count);
|
||||
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
|
||||
LLVMTypeRef res_type = LLVMTypeOf(res.value);
|
||||
GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
|
||||
return res;
|
||||
} else {
|
||||
// TODO(bill): THIS IS HACK BUT IT WORKS FOR WHAT I NEED
|
||||
@@ -1517,7 +1694,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
new_values[i] = LLVMConstNull(LLVMTypeOf(old_value));
|
||||
}
|
||||
}
|
||||
LLVMValueRef constant_value = llvm_const_named_struct_internal(struct_type, new_values, cast(unsigned)value_count);
|
||||
LLVMValueRef constant_value = llvm_const_named_struct_internal(m, struct_type, new_values, cast(unsigned)value_count);
|
||||
|
||||
GB_ASSERT(is_local);
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
@@ -1611,7 +1788,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
|
||||
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
i64 offset = matrix_row_major_index_to_offset(type, k);
|
||||
GB_ASSERT(values[offset] == nullptr);
|
||||
@@ -1623,7 +1800,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
GB_ASSERT(index < max_count);
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
i64 offset = matrix_row_major_index_to_offset(type, index);
|
||||
GB_ASSERT(values[offset] == nullptr);
|
||||
values[offset] = val;
|
||||
@@ -1647,7 +1824,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
i64 offset = 0;
|
||||
offset = matrix_row_major_index_to_offset(type, i);
|
||||
values[offset] = lb_const_value(m, elem_type, tav.value, cc).value;
|
||||
values[offset] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
|
||||
}
|
||||
for (isize i = 0; i < total_count; i++) {
|
||||
if (values[i] == nullptr) {
|
||||
|
||||
@@ -1327,7 +1327,7 @@ gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *
|
||||
}
|
||||
lbModule *m = &gen->default_module;
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
m = lb_module_of_entity(gen, e);
|
||||
m = lb_module_of_entity(gen, e, m);
|
||||
}
|
||||
GB_ASSERT(m != nullptr);
|
||||
|
||||
|
||||
@@ -2502,7 +2502,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
}
|
||||
for (Type *vt : dst->Union.variants) {
|
||||
if (src_type == t_llvm_bool && is_type_boolean(vt)) {
|
||||
value = lb_emit_conv(p, value, vt);
|
||||
lbAddr parent = lb_add_local_generated(p, t, true);
|
||||
lb_emit_store_union_variant(p, parent.addr, value, vt);
|
||||
return lb_addr_load(p, parent);
|
||||
@@ -2563,10 +2562,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
|
||||
Type *dt = t;
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
|
||||
Selection sel = {};
|
||||
sel.index.allocator = heap_allocator();
|
||||
defer (array_free(&sel.index));
|
||||
sel.index.allocator = temporary_allocator();
|
||||
if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
|
||||
if (sel.entity == nullptr) {
|
||||
GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
|
||||
@@ -3929,6 +3929,20 @@ gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal Type *lb_build_expr_original_const_type(Ast *expr) {
|
||||
expr = unparen_expr(expr);
|
||||
Type *type = type_of_expr(expr);
|
||||
if (is_type_union(type)) {
|
||||
if (expr->kind == Ast_CallExpr) {
|
||||
if (expr->CallExpr.proc->tav.mode == Addressing_Type) {
|
||||
Type *res = lb_build_expr_original_const_type(expr->CallExpr.args[0]);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return type_of_expr(expr);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
@@ -3940,9 +3954,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
|
||||
|
||||
|
||||
|
||||
if (tv.value.kind != ExactValue_Invalid) {
|
||||
Type *original_type = lb_build_expr_original_const_type(expr);
|
||||
// NOTE(bill): Short on constant values
|
||||
return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
|
||||
return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, original_type);
|
||||
} else if (tv.mode == Addressing_Type) {
|
||||
// NOTE(bill, 2023-01-16): is this correct? I hope so at least
|
||||
return lb_typeid(m, tv.type);
|
||||
@@ -4023,7 +4039,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
TypeAndValue tav = type_and_value_of_expr(expr);
|
||||
GB_ASSERT(tav.mode == Addressing_Constant);
|
||||
|
||||
return lb_const_value(p->module, type, tv.value);
|
||||
return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, tv.type);
|
||||
case_end;
|
||||
|
||||
case_ast_node(se, SelectorCallExpr, expr);
|
||||
@@ -4304,7 +4320,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->kind == Entity_Constant) {
|
||||
Type *t = default_type(type_of_expr(expr));
|
||||
lbValue v = lb_const_value(p->module, t, e->Constant.value);
|
||||
lbValue v = lb_const_value(p->module, t, e->Constant.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL, e->type);
|
||||
if (LLVMIsConstant(v.value)) {
|
||||
lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
|
||||
return g;
|
||||
|
||||
+279
-62
@@ -15,7 +15,9 @@ gb_global isize lb_global_type_info_member_offsets_index = 0;
|
||||
gb_global isize lb_global_type_info_member_usings_index = 0;
|
||||
gb_global isize lb_global_type_info_member_tags_index = 0;
|
||||
|
||||
gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) {
|
||||
lbModule *m = cast(lbModule *)data;
|
||||
Checker *c = m->checker;
|
||||
m->info = &c->info;
|
||||
|
||||
|
||||
@@ -46,6 +48,12 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
}
|
||||
module_name = gb_string_appendc(module_name, "builtin");
|
||||
}
|
||||
if (m->polymorphic_module == m) {
|
||||
if (gb_string_length(module_name)) {
|
||||
module_name = gb_string_appendc(module_name, "-");
|
||||
}
|
||||
module_name = gb_string_appendc(module_name, "$parapoly");
|
||||
}
|
||||
|
||||
m->module_name = module_name;
|
||||
m->ctx = LLVMContextCreate();
|
||||
@@ -89,15 +97,19 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
map_init(&m->function_type_map);
|
||||
string_map_init(&m->gen_procs);
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
array_init(&m->procedures_to_generate, a, 0, 1<<10);
|
||||
mpsc_init(&m->procedures_to_generate, a);
|
||||
map_init(&m->procedure_values, 1<<11);
|
||||
array_init(&m->generated_procedures, a, 0, 1<<10);
|
||||
} else {
|
||||
array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count);
|
||||
mpsc_init(&m->procedures_to_generate, a);
|
||||
map_init(&m->procedure_values, c->info.all_procedures.count*2);
|
||||
array_init(&m->generated_procedures, a, 0, c->info.all_procedures.count*2);
|
||||
}
|
||||
|
||||
|
||||
array_init(&m->global_procedures_to_create, a, 0, 1024);
|
||||
array_init(&m->global_types_to_create, a, 0, 1024);
|
||||
array_init(&m->missing_procedures_to_check, a, 0, 16);
|
||||
mpsc_init(&m->missing_procedures_to_check, a);
|
||||
map_init(&m->debug_values);
|
||||
|
||||
string_map_init(&m->objc_classes);
|
||||
@@ -113,6 +125,15 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
|
||||
m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal void lb_init_module(lbModule *m, bool do_threading) {
|
||||
if (do_threading) {
|
||||
thread_pool_add_task(lb_init_module_worker_proc, m);
|
||||
} else {
|
||||
lb_init_module_worker_proc(m);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
@@ -125,6 +146,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isize thread_count = gb_max(build_context.thread_count, 1);
|
||||
isize worker_count = thread_count-1;
|
||||
bool do_threading = !!(LLVMIsMultithreaded() && USE_SEPARATE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0);
|
||||
|
||||
String init_fullpath = c->parser->init_fullpath;
|
||||
linker_data_init(gen, &c->info, init_fullpath);
|
||||
|
||||
@@ -136,7 +161,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
|
||||
map_init(&gen->modules, gen->info->packages.count*2);
|
||||
map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
|
||||
map_init(&gen->anonymous_proc_lits, 1024);
|
||||
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0;
|
||||
@@ -145,26 +169,94 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
auto m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
m->pkg = pkg;
|
||||
m->gen = gen;
|
||||
m->checker = c;
|
||||
map_set(&gen->modules, cast(void *)pkg, m);
|
||||
lb_init_module(m, c);
|
||||
if (!module_per_file) {
|
||||
lb_init_module(m, do_threading);
|
||||
|
||||
if (LLVM_WEAK_MONOMORPHIZATION) {
|
||||
auto pm = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
pm->pkg = pkg;
|
||||
pm->gen = gen;
|
||||
pm->checker = c;
|
||||
m->polymorphic_module = pm;
|
||||
pm->polymorphic_module = pm;
|
||||
|
||||
map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
|
||||
|
||||
lb_init_module(pm, do_threading);
|
||||
}
|
||||
|
||||
bool allow_for_per_file = pkg->kind == Package_Runtime || module_per_file;
|
||||
|
||||
#if 0
|
||||
if (!allow_for_per_file) {
|
||||
if (pkg->files.count >= 20) {
|
||||
isize proc_count = 0;
|
||||
for (Entity *e : gen->info->entities) {
|
||||
if (e->kind != Entity_Procedure) {
|
||||
continue;
|
||||
}
|
||||
if (e->Procedure.is_foreign) {
|
||||
continue;
|
||||
}
|
||||
if (e->pkg == pkg) {
|
||||
proc_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_count >= 300) {
|
||||
allow_for_per_file = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!allow_for_per_file) {
|
||||
continue;
|
||||
}
|
||||
// NOTE(bill): Probably per file is not a good idea, so leave this for later
|
||||
for (AstFile *file : pkg->files) {
|
||||
auto m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
auto m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
m->file = file;
|
||||
m->pkg = pkg;
|
||||
m->gen = gen;
|
||||
m->pkg = pkg;
|
||||
m->gen = gen;
|
||||
m->checker = c;
|
||||
map_set(&gen->modules, cast(void *)file, m);
|
||||
lb_init_module(m, c);
|
||||
lb_init_module(m, do_threading);
|
||||
|
||||
|
||||
if (LLVM_WEAK_MONOMORPHIZATION) {
|
||||
auto pm = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
pm->file = file;
|
||||
pm->pkg = pkg;
|
||||
pm->gen = gen;
|
||||
pm->checker = c;
|
||||
m->polymorphic_module = pm;
|
||||
pm->polymorphic_module = pm;
|
||||
|
||||
map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
|
||||
|
||||
lb_init_module(pm, do_threading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LLVM_WEAK_MONOMORPHIZATION) {
|
||||
lbModule *m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
gen->equal_module = m;
|
||||
m->gen = gen;
|
||||
m->checker = c;
|
||||
map_set(&gen->modules, cast(void *)m, m); // point to itself just add it to the list
|
||||
lb_init_module(m, do_threading);
|
||||
}
|
||||
}
|
||||
|
||||
gen->default_module.gen = gen;
|
||||
gen->default_module.checker = c;
|
||||
map_set(&gen->modules, cast(void *)1, &gen->default_module);
|
||||
lb_init_module(&gen->default_module, c);
|
||||
lb_init_module(&gen->default_module, do_threading);
|
||||
|
||||
thread_pool_wait();
|
||||
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
@@ -403,9 +495,9 @@ gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) {
|
||||
return &gen->default_module;
|
||||
}
|
||||
|
||||
gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
gb_internal lbModule *lb_module_of_entity_internal(lbGenerator *gen, Entity *e, lbModule *curr_module) {
|
||||
lbModule **found = nullptr;
|
||||
|
||||
if (e->kind == Entity_Procedure &&
|
||||
e->decl_info &&
|
||||
e->decl_info->code_gen_module) {
|
||||
@@ -428,6 +520,22 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
|
||||
return &gen->default_module;
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e, lbModule *curr_module) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
GB_ASSERT(curr_module != nullptr);
|
||||
lbModule *m = lb_module_of_entity_internal(gen, e, curr_module);
|
||||
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
if (e->kind == Entity_Procedure && e->Procedure.generated_from_polymorphic) {
|
||||
if (m->polymorphic_module) {
|
||||
return m->polymorphic_module;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_addr(lbValue addr) {
|
||||
lbAddr v = {lbAddr_Default, addr};
|
||||
return v;
|
||||
@@ -1408,8 +1516,11 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
|
||||
unsigned element_count = LLVMCountStructElementTypes(uvt);
|
||||
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
|
||||
|
||||
LLVMValueRef ptr = u.value;
|
||||
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
|
||||
|
||||
lbValue tag_ptr = {};
|
||||
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, u.value, 1, "");
|
||||
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
|
||||
tag_ptr.type = alloc_type_pointer(tag_type);
|
||||
return tag_ptr;
|
||||
}
|
||||
@@ -1634,8 +1745,92 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
|
||||
map_set(&m->func_raw_types, type, new_abi_fn_type);
|
||||
|
||||
return new_abi_fn_type;
|
||||
|
||||
}
|
||||
|
||||
|
||||
gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(type->kind == Type_Union);
|
||||
|
||||
if (type->Union.variants.count <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (type->Union.variants.count == 1) {
|
||||
return lb_type(m, type->Union.variants[0]);
|
||||
}
|
||||
|
||||
i64 align = type_align_of(type);
|
||||
|
||||
unsigned block_size = cast(unsigned)type->Union.variant_block_size;
|
||||
if (block_size == 0) {
|
||||
return lb_type_padding_filler(m, block_size, align);
|
||||
}
|
||||
|
||||
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) {
|
||||
return lb_type(m, t_rawptr);
|
||||
}
|
||||
|
||||
{
|
||||
Type *pt = type->Union.variants[0];
|
||||
for (isize i = 1; i < type->Union.variants.count; i++) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (!are_types_identical(pt, t)) {
|
||||
goto end_check_for_all_the_same;
|
||||
}
|
||||
}
|
||||
return lb_type(m, pt);
|
||||
} end_check_for_all_the_same:;
|
||||
|
||||
{
|
||||
Type *first_different = nullptr;
|
||||
for (isize i = 0; i < type->Union.variants.count; i++) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (type_size_of(t) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (first_different == nullptr) {
|
||||
first_different = t;
|
||||
} else if (!are_types_identical(first_different, t)) {
|
||||
goto end_rest_zero_except_one;
|
||||
}
|
||||
}
|
||||
if (first_different != nullptr) {
|
||||
return lb_type(m, first_different);
|
||||
}
|
||||
} end_rest_zero_except_one:;
|
||||
|
||||
// {
|
||||
// LLVMTypeRef first_different = nullptr;
|
||||
// for (isize i = 0; i < type->Union.variants.count; i++) {
|
||||
// Type *t = type->Union.variants[i];
|
||||
// if (type_size_of(t) == 0) {
|
||||
// continue;
|
||||
// }
|
||||
// if (first_different == nullptr) {
|
||||
// first_different = lb_type(m, base_type(t));
|
||||
// } else {
|
||||
// LLVMTypeRef llvm_t = lb_type(m, base_type(t));
|
||||
// if (llvm_t != first_different) {
|
||||
// goto end_rest_zero_except_one_llvm_like;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (first_different != nullptr) {
|
||||
// return first_different;
|
||||
// }
|
||||
// } end_rest_zero_except_one_llvm_like:;
|
||||
|
||||
|
||||
return lb_type_padding_filler(m, block_size, align);
|
||||
}
|
||||
|
||||
|
||||
gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
i64 size = type_size_of(type); // Check size
|
||||
@@ -2148,27 +2343,24 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
|
||||
}
|
||||
|
||||
unsigned block_size = cast(unsigned)type->Union.variant_block_size;
|
||||
|
||||
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
|
||||
if (is_type_union_maybe_pointer(type)) {
|
||||
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
|
||||
array_add(&fields, variant);
|
||||
} else {
|
||||
LLVMTypeRef block_type = nullptr;
|
||||
} else if (type->Union.variants.count == 1) {
|
||||
LLVMTypeRef block_type = lb_type(m, type->Union.variants[0]);
|
||||
|
||||
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);
|
||||
i64 padding = size - used_size;
|
||||
if (padding > 0) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
|
||||
array_add(&fields, padding_type);
|
||||
}
|
||||
} else {
|
||||
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type);
|
||||
|
||||
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
|
||||
array_add(&fields, block_type);
|
||||
@@ -2914,7 +3106,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
|
||||
return lb_find_procedure_value_from_entity(m, e);
|
||||
}
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, e);
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, e, m);
|
||||
if (other_module != m) {
|
||||
String name = lb_get_entity_name(other_module, e);
|
||||
|
||||
@@ -2962,7 +3154,7 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
|
||||
|
||||
lbModule *other_module = m;
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
other_module = lb_module_of_entity(gen, e);
|
||||
other_module = lb_module_of_entity(gen, e, m);
|
||||
}
|
||||
if (other_module == m) {
|
||||
debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m);
|
||||
@@ -2979,9 +3171,6 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
|
||||
}
|
||||
|
||||
if (ignore_body) {
|
||||
mutex_lock(&gen->anonymous_proc_lits_mutex);
|
||||
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
|
||||
|
||||
GB_ASSERT(other_module != nullptr);
|
||||
rw_mutex_shared_lock(&other_module->values_mutex);
|
||||
auto *found = map_get(&other_module->values, e);
|
||||
@@ -2989,10 +3178,10 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
|
||||
if (found == nullptr) {
|
||||
// THIS IS THE RACE CONDITION
|
||||
lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false);
|
||||
array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
|
||||
mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
|
||||
}
|
||||
} else {
|
||||
array_add(&m->missing_procedures_to_check, missing_proc);
|
||||
mpsc_enqueue(&m->missing_procedures_to_check, missing_proc);
|
||||
}
|
||||
|
||||
rw_mutex_shared_lock(&m->values_mutex);
|
||||
@@ -3010,18 +3199,16 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
|
||||
|
||||
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
|
||||
lbGenerator *gen = m->gen;
|
||||
|
||||
mutex_lock(&gen->anonymous_proc_lits_mutex);
|
||||
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
|
||||
|
||||
TokenPos pos = ast_token(expr).pos;
|
||||
lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr);
|
||||
if (found) {
|
||||
return lb_find_procedure_value_from_entity(m, (*found)->entity);
|
||||
}
|
||||
gb_unused(gen);
|
||||
|
||||
ast_node(pl, ProcLit, expr);
|
||||
|
||||
if (pl->decl->entity.load() != nullptr) {
|
||||
return lb_find_procedure_value_from_entity(m, pl->decl->entity.load());
|
||||
}
|
||||
|
||||
TokenPos pos = ast_token(expr).pos;
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent$count
|
||||
isize name_len = prefix_name.len + 6 + 11;
|
||||
@@ -3039,30 +3226,51 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
|
||||
token.string = name;
|
||||
Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
|
||||
e->file = expr->file();
|
||||
e->scope = e->file->scope;
|
||||
|
||||
lbModule *target_module = m;
|
||||
GB_ASSERT(target_module != nullptr);
|
||||
|
||||
// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
|
||||
pl->decl->code_gen_module = m;
|
||||
pl->decl->code_gen_module = target_module;
|
||||
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);
|
||||
GB_ASSERT(e->code_gen_module == m);
|
||||
pl->decl->entity.store(e);
|
||||
|
||||
lbValue value = {};
|
||||
value.value = p->value;
|
||||
value.type = p->type;
|
||||
|
||||
map_set(&gen->anonymous_proc_lits, expr, p);
|
||||
array_add(&m->procedures_to_generate, p);
|
||||
if (parent != nullptr) {
|
||||
array_add(&parent->children, p);
|
||||
if (target_module != m) {
|
||||
rw_mutex_shared_lock(&target_module->values_mutex);
|
||||
lbValue *found = map_get(&target_module->values, e);
|
||||
rw_mutex_shared_unlock(&target_module->values_mutex);
|
||||
if (found == nullptr) {
|
||||
lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false);
|
||||
mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module);
|
||||
}
|
||||
|
||||
lbProcedure *p = lb_create_procedure(m, e, true);
|
||||
|
||||
lbValue value = {};
|
||||
value.value = p->value;
|
||||
value.type = p->type;
|
||||
return value;
|
||||
} else {
|
||||
string_map_set(&m->members, name, value);
|
||||
lbProcedure *p = lb_create_procedure(m, e);
|
||||
|
||||
lbValue value = {};
|
||||
value.value = p->value;
|
||||
value.type = p->type;
|
||||
|
||||
mpsc_enqueue(&m->procedures_to_generate, p);
|
||||
if (parent != nullptr) {
|
||||
array_add(&parent->children, p);
|
||||
} else {
|
||||
string_map_set(&m->members, name, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -3071,11 +3279,18 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = default_type(type);
|
||||
|
||||
LLVMTypeRef actual_type = lb_type(m, type);
|
||||
if (value.value != nullptr) {
|
||||
LLVMTypeRef value_type = LLVMTypeOf(value.value);
|
||||
GB_ASSERT_MSG(lb_sizeof(actual_type) == lb_sizeof(value_type), "%s vs %s", LLVMPrintTypeToString(actual_type), LLVMPrintTypeToString(value_type));
|
||||
actual_type = value_type;
|
||||
}
|
||||
|
||||
Scope *scope = nullptr;
|
||||
Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
|
||||
lbValue g = {};
|
||||
g.type = alloc_type_pointer(type);
|
||||
g.value = LLVMAddGlobal(m->mod, lb_type(m, type), alloc_cstring(temporary_allocator(), name));
|
||||
g.value = LLVMAddGlobal(m->mod, actual_type, alloc_cstring(temporary_allocator(), name));
|
||||
if (value.value != nullptr) {
|
||||
GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value));
|
||||
LLVMSetInitializer(g.value, value.value);
|
||||
@@ -3083,6 +3298,8 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
|
||||
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type)));
|
||||
}
|
||||
|
||||
g.value = LLVMConstPointerCast(g.value, lb_type(m, g.type));
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
lb_add_member(m, name, g);
|
||||
|
||||
@@ -3145,7 +3362,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
}
|
||||
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, e);
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, e, m);
|
||||
|
||||
bool is_external = other_module != m;
|
||||
if (!is_external) {
|
||||
|
||||
+13
-14
@@ -84,7 +84,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
String link_name = {};
|
||||
|
||||
if (ignore_body) {
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, entity);
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, entity, m);
|
||||
link_name = lb_get_entity_name(other_module, entity);
|
||||
} else {
|
||||
link_name = lb_get_entity_name(m, entity);
|
||||
@@ -99,7 +99,6 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
|
||||
|
||||
p->module = m;
|
||||
@@ -837,9 +836,8 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
|
||||
gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
|
||||
GB_ASSERT(pd->body != nullptr);
|
||||
lbModule *m = p->module;
|
||||
auto *min_dep_set = &m->info->minimum_dependency_set;
|
||||
|
||||
if (ptr_set_exists(min_dep_set, e) == false) {
|
||||
if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
|
||||
// NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
return;
|
||||
}
|
||||
@@ -875,7 +873,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
|
||||
|
||||
lb_add_entity(m, e, value);
|
||||
array_add(&p->children, nested_proc);
|
||||
array_add(&m->procedures_to_generate, nested_proc);
|
||||
mpsc_enqueue(&m->procedures_to_generate, nested_proc);
|
||||
}
|
||||
|
||||
|
||||
@@ -962,13 +960,14 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
|
||||
arg_type != param_type) {
|
||||
LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
|
||||
LLVMTypeKind param_kind = LLVMGetTypeKind(param_type);
|
||||
if (arg_kind == param_kind &&
|
||||
arg_kind == LLVMPointerTypeKind) {
|
||||
// NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times
|
||||
// I don't know why...
|
||||
args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, "");
|
||||
arg_type = param_type;
|
||||
continue;
|
||||
if (arg_kind == param_kind) {
|
||||
if (arg_kind == LLVMPointerTypeKind) {
|
||||
// NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times
|
||||
// I don't know why...
|
||||
args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, "");
|
||||
arg_type = param_type;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2251,7 +2250,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
|
||||
procedure = e->parent_proc_decl->entity->token.string;
|
||||
procedure = e->parent_proc_decl->entity.load()->token.string;
|
||||
} else {
|
||||
procedure = str_lit("");
|
||||
}
|
||||
@@ -2278,7 +2277,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
elements[i] = element;
|
||||
}
|
||||
|
||||
LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count);
|
||||
LLVMValueRef backing_array = llvm_const_array(m, 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_from_procedure(p, array_type, {backing_array, array_type});
|
||||
|
||||
@@ -3,8 +3,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
return;
|
||||
}
|
||||
|
||||
auto *min_dep_set = &p->module->info->minimum_dependency_set;
|
||||
|
||||
for (Ast *ident : vd->names) {
|
||||
GB_ASSERT(ident->kind == Ast_Ident);
|
||||
Entity *e = entity_of_node(ident);
|
||||
@@ -21,7 +19,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
}
|
||||
}
|
||||
|
||||
if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
|
||||
if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -56,7 +54,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
if (gpd) {
|
||||
rw_mutex_shared_lock(&gpd->mutex);
|
||||
for (Entity *e : gpd->procs) {
|
||||
if (!ptr_set_exists(min_dep_set, e)) {
|
||||
if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
|
||||
continue;
|
||||
}
|
||||
DeclInfo *d = decl_info_of_entity(e);
|
||||
@@ -94,7 +92,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
value.value = nested_proc->value;
|
||||
value.type = nested_proc->type;
|
||||
|
||||
array_add(&p->module->procedures_to_generate, nested_proc);
|
||||
mpsc_enqueue(&p->module->procedures_to_generate, nested_proc);
|
||||
array_add(&p->children, nested_proc);
|
||||
string_map_set(&p->module->members, name, value);
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
(name##_values)[i] = LLVMConstNull(elem); \
|
||||
} \
|
||||
} \
|
||||
LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \
|
||||
LLVMSetInitializer(name.addr.value, llvm_const_array(m, elem, name##_values, at->Array.count)); \
|
||||
})
|
||||
|
||||
type_info_allocate_values(lb_global_type_info_member_types);
|
||||
@@ -394,8 +394,9 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
String proc_name = {};
|
||||
if (t->Named.type_name->parent_proc_decl) {
|
||||
DeclInfo *decl = t->Named.type_name->parent_proc_decl;
|
||||
if (decl->entity && decl->entity->kind == Entity_Procedure) {
|
||||
proc_name = decl->entity->token.string;
|
||||
Entity *e = decl->entity.load();
|
||||
if (e && e->kind == Entity_Procedure) {
|
||||
proc_name = e->token.string;
|
||||
}
|
||||
}
|
||||
TokenPos pos = t->Named.type_name->token.pos;
|
||||
@@ -751,8 +752,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
|
||||
}
|
||||
|
||||
LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count);
|
||||
LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
|
||||
LLVMValueRef name_init = llvm_const_array(m, lb_type(m, t_string), name_values, cast(unsigned)fields.count);
|
||||
LLVMValueRef value_init = llvm_const_array(m, lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
|
||||
LLVMSetInitializer(name_array.value, name_init);
|
||||
LLVMSetInitializer(value_array.value, value_init);
|
||||
LLVMSetGlobalConstant(name_array.value, true);
|
||||
|
||||
@@ -2787,3 +2787,292 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
|
||||
ExactValue value = type_and_value_of_expr(expr).value;
|
||||
return llvm_atomic_ordering_from_odin(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct lbDiagParaPolyEntry {
|
||||
Entity *entity;
|
||||
String canonical_name;
|
||||
isize count;
|
||||
isize total_code_size;
|
||||
};
|
||||
|
||||
gb_internal isize lb_total_code_size(lbProcedure *p) {
|
||||
isize instruction_count = 0;
|
||||
|
||||
LLVMBasicBlockRef first = LLVMGetFirstBasicBlock(p->value);
|
||||
|
||||
for (LLVMBasicBlockRef block = first; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
|
||||
for (LLVMValueRef instr = LLVMGetFirstInstruction(block); instr != nullptr; instr = LLVMGetNextInstruction(instr)) {
|
||||
instruction_count += 1;
|
||||
}
|
||||
}
|
||||
return instruction_count;
|
||||
|
||||
}
|
||||
|
||||
gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
|
||||
PtrMap<Entity * /* Parent */, lbDiagParaPolyEntry> procs = {};
|
||||
map_init(&procs);
|
||||
defer (map_destroy(&procs));
|
||||
|
||||
for (auto &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
for (lbProcedure *p : m->generated_procedures) {
|
||||
Entity *e = p->entity;
|
||||
if (e == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (p->builder == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DeclInfo *d = e->decl_info;
|
||||
Entity *para_poly_parent = d->para_poly_original;
|
||||
if (para_poly_parent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lbDiagParaPolyEntry *entry = map_get(&procs, para_poly_parent);
|
||||
if (entry == nullptr) {
|
||||
lbDiagParaPolyEntry entry = {};
|
||||
entry.entity = para_poly_parent;
|
||||
entry.count = 0;
|
||||
|
||||
|
||||
gbString w = string_canonical_entity_name(permanent_allocator(), entry.entity);
|
||||
String name = make_string_c(w);
|
||||
|
||||
for (isize i = 0; i < name.len; i++) {
|
||||
String s = substring(name, i, name.len);
|
||||
if (string_starts_with(s, str_lit(":proc"))) {
|
||||
name = substring(name, 0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entry.canonical_name = name;
|
||||
|
||||
map_set(&procs, para_poly_parent, entry);
|
||||
}
|
||||
entry = map_get(&procs, para_poly_parent);
|
||||
GB_ASSERT(entry != nullptr);
|
||||
entry->count += 1;
|
||||
entry->total_code_size += lb_total_code_size(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto entries = array_make<lbDiagParaPolyEntry>(heap_allocator(), 0, procs.count);
|
||||
defer (array_free(&entries));
|
||||
|
||||
for (auto &entry : procs) {
|
||||
array_add(&entries, entry.value);
|
||||
}
|
||||
|
||||
array_sort(entries, [](void const *a, void const *b) -> int {
|
||||
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
|
||||
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
|
||||
if (x->total_code_size > y->total_code_size) {
|
||||
return -1;
|
||||
}
|
||||
if (x->total_code_size < y->total_code_size) {
|
||||
return +1;
|
||||
}
|
||||
return string_compare(x->canonical_name, y->canonical_name);
|
||||
});
|
||||
|
||||
|
||||
gb_printf("Parametric Polymorphic Procedure Diagnostics\n");
|
||||
gb_printf("------------------------------------------------------------------------------------------\n");
|
||||
|
||||
gb_printf("Sorted by Total Instruction Count Descending (Top 100)\n\n");
|
||||
gb_printf("Total Instruction Count | Instantiation Count | Average Instruction Count | Procedure Name\n");
|
||||
|
||||
isize max_count = 100;
|
||||
for (auto &entry : entries) {
|
||||
isize code_size = entry.total_code_size;
|
||||
isize count = entry.count;
|
||||
String name = entry.canonical_name;
|
||||
|
||||
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
|
||||
|
||||
gb_printf("%23td | %19td | %25.2f | %.*s\n", code_size, count, average, LIT(name));
|
||||
if (max_count-- <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gb_printf("------------------------------------------------------------------------------------------\n");
|
||||
|
||||
array_sort(entries, [](void const *a, void const *b) -> int {
|
||||
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
|
||||
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
|
||||
if (x->count > y->count) {
|
||||
return -1;
|
||||
}
|
||||
if (x->count < y->count) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
return string_compare(x->canonical_name, y->canonical_name);
|
||||
});
|
||||
|
||||
gb_printf("Sorted by Total Instantiation Count Descending (Top 100)\n\n");
|
||||
gb_printf("Instantiation Count | Total Instruction Count | Average Instruction Count | Procedure Name\n");
|
||||
|
||||
max_count = 100;
|
||||
for (auto &entry : entries) {
|
||||
isize code_size = entry.total_code_size;
|
||||
isize count = entry.count;
|
||||
String name = entry.canonical_name;
|
||||
|
||||
|
||||
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
|
||||
|
||||
gb_printf("%19td | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
|
||||
if (max_count-- <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gb_printf("------------------------------------------------------------------------------------------\n");
|
||||
|
||||
|
||||
array_sort(entries, [](void const *a, void const *b) -> int {
|
||||
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
|
||||
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
|
||||
if (x->count < y->count) {
|
||||
return -1;
|
||||
}
|
||||
if (x->count > y->count) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
if (x->total_code_size > y->total_code_size) {
|
||||
return -1;
|
||||
}
|
||||
if (x->total_code_size < y->total_code_size) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
return string_compare(x->canonical_name, y->canonical_name);
|
||||
});
|
||||
|
||||
gb_printf("Single Instanced Parametric Polymorphic Procedures\n\n");
|
||||
gb_printf("Instruction Count | Procedure Name\n");
|
||||
for (auto &entry : entries) {
|
||||
isize code_size = entry.total_code_size;
|
||||
isize count = entry.count;
|
||||
String name = entry.canonical_name;
|
||||
if (count != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
gb_printf("%17td | %.*s\n", code_size, LIT(name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct lbDiagModuleEntry {
|
||||
lbModule *m;
|
||||
String name;
|
||||
isize global_internal_count;
|
||||
isize global_external_count;
|
||||
isize proc_internal_count;
|
||||
isize proc_external_count;
|
||||
isize total_instruction_count;
|
||||
};
|
||||
|
||||
gb_internal void lb_do_module_diagnostics(lbGenerator *gen) {
|
||||
Array<lbDiagModuleEntry> modules = {};
|
||||
array_init(&modules, heap_allocator());
|
||||
defer (array_free(&modules));
|
||||
|
||||
for (auto &em : gen->modules) {
|
||||
lbModule *m = em.value;
|
||||
|
||||
{
|
||||
lbDiagModuleEntry entry = {};
|
||||
entry.m = m;
|
||||
entry.name = make_string_c(m->module_name);
|
||||
array_add(&modules, entry);
|
||||
}
|
||||
lbDiagModuleEntry &entry = modules[modules.count-1];
|
||||
|
||||
for (LLVMValueRef p = LLVMGetFirstFunction(m->mod); p != nullptr; p = LLVMGetNextFunction(p)) {
|
||||
LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(p);
|
||||
if (block == nullptr) {
|
||||
entry.proc_external_count += 1;
|
||||
} else {
|
||||
entry.proc_internal_count += 1;
|
||||
|
||||
for (; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
|
||||
for (LLVMValueRef i = LLVMGetFirstInstruction(block); i != nullptr; i = LLVMGetNextInstruction(i)) {
|
||||
entry.total_instruction_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (LLVMValueRef g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
|
||||
LLVMLinkage linkage = LLVMGetLinkage(g);
|
||||
if (linkage == LLVMExternalLinkage) {
|
||||
entry.global_external_count += 1;
|
||||
} else {
|
||||
entry.global_internal_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array_sort(modules, [](void const *a, void const *b) -> int {
|
||||
lbDiagModuleEntry *x = cast(lbDiagModuleEntry *)a;
|
||||
lbDiagModuleEntry *y = cast(lbDiagModuleEntry *)b;
|
||||
|
||||
if (x->total_instruction_count > y->total_instruction_count) {
|
||||
return -1;
|
||||
}
|
||||
if (x->total_instruction_count < y->total_instruction_count) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
return string_compare(x->name, y->name);
|
||||
});
|
||||
|
||||
gb_printf("Module Diagnostics\n\n");
|
||||
gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Files | Instructions/File | Instructions/Proc | Module Name\n");
|
||||
gb_printf("-------------------+------------------+------------------+----------------+----------------+-------+-------------------+-------------------+------------\n");
|
||||
for (auto &entry : modules) {
|
||||
isize file_count = 1;
|
||||
if (entry.m->file != nullptr) {
|
||||
file_count = 1;
|
||||
} else if (entry.m->pkg) {
|
||||
file_count = entry.m->pkg->files.count;
|
||||
}
|
||||
|
||||
f64 instructions_per_file = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)file_count);
|
||||
f64 instructions_per_proc = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)entry.proc_internal_count);
|
||||
|
||||
gb_printf("%18td | %16td | %16td | %14td | %14td | %5td | %17.1f | %17.1f | %s \n",
|
||||
entry.total_instruction_count,
|
||||
entry.global_internal_count,
|
||||
entry.global_external_count,
|
||||
entry.proc_internal_count,
|
||||
entry.proc_external_count,
|
||||
file_count,
|
||||
instructions_per_file,
|
||||
instructions_per_proc,
|
||||
entry.m->module_name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
gb_internal void lb_do_build_diagnostics(lbGenerator *gen) {
|
||||
lb_do_para_poly_diagnostics(gen);
|
||||
gb_printf("------------------------------------------------------------------------------------------\n");
|
||||
gb_printf("------------------------------------------------------------------------------------------\n\n");
|
||||
lb_do_module_diagnostics(gen);
|
||||
gb_printf("------------------------------------------------------------------------------------------\n");
|
||||
gb_printf("------------------------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
+26
-2
@@ -394,6 +394,8 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_IntegerDivisionByZero,
|
||||
|
||||
BuildFlag_BuildDiagnostics,
|
||||
|
||||
// internal use only
|
||||
BuildFlag_InternalFastISel,
|
||||
BuildFlag_InternalIgnoreLazy,
|
||||
@@ -403,6 +405,8 @@ enum BuildFlagKind {
|
||||
BuildFlag_InternalCached,
|
||||
BuildFlag_InternalNoInline,
|
||||
BuildFlag_InternalByValue,
|
||||
BuildFlag_InternalWeakMonomorphization,
|
||||
BuildFlag_InternalLLVMVerification,
|
||||
|
||||
BuildFlag_Tilde,
|
||||
|
||||
@@ -617,6 +621,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
|
||||
@@ -626,6 +631,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all);
|
||||
|
||||
#if ALLOW_TILDE
|
||||
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
|
||||
@@ -1558,6 +1565,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
break;
|
||||
|
||||
case BuildFlag_BuildDiagnostics:
|
||||
build_context.build_diagnostics = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_InternalFastISel:
|
||||
build_context.fast_isel = true;
|
||||
break;
|
||||
@@ -1584,6 +1595,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_InternalByValue:
|
||||
build_context.internal_by_value = true;
|
||||
break;
|
||||
case BuildFlag_InternalWeakMonomorphization:
|
||||
build_context.internal_weak_monomorphization = true;
|
||||
break;
|
||||
case BuildFlag_InternalLLVMVerification:
|
||||
build_context.internal_ignore_llvm_verification = true;
|
||||
break;
|
||||
|
||||
|
||||
case BuildFlag_Tilde:
|
||||
build_context.tilde_backend = true;
|
||||
@@ -2826,7 +2844,7 @@ gb_internal int print_show_help(String const arg0, String command, String option
|
||||
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons.");
|
||||
print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
|
||||
print_usage_line(2, "Errs on deprecated syntax.");
|
||||
print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS).");
|
||||
print_usage_line(2, "Errs when the attached-brace style is not adhered to (also known as 1TBS).");
|
||||
print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token.");
|
||||
}
|
||||
}
|
||||
@@ -3050,7 +3068,8 @@ gb_internal void print_show_unused(Checker *c) {
|
||||
if (e->token.string == "_") {
|
||||
continue;
|
||||
}
|
||||
if (ptr_set_exists(&info->minimum_dependency_set, e)) {
|
||||
|
||||
if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
|
||||
continue;
|
||||
}
|
||||
array_add(&unused, e);
|
||||
@@ -3617,6 +3636,11 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// Warn about Windows i386 thread-local storage limitations
|
||||
if (build_context.metrics.arch == TargetArch_i386 && build_context.metrics.os == TargetOs_windows) {
|
||||
gb_printf_err("Warning: Thread-local storage is disabled on Windows i386.\n");
|
||||
}
|
||||
|
||||
// Check chosen microarchitecture. If not found or ?, print list.
|
||||
bool print_microarch_list = true;
|
||||
|
||||
@@ -57,10 +57,13 @@ gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) {
|
||||
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)) {
|
||||
auto *e = &s->keys[hash_index];
|
||||
u64 hash = e->hash;
|
||||
Type *key = e->type;
|
||||
if (hash == pair.hash &&
|
||||
are_types_identical_unique_tuples(key, pair.type)) {
|
||||
return hash_index;
|
||||
} else if (key == 0) {
|
||||
} else if (key == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
@@ -164,6 +167,48 @@ gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it
|
||||
return type_set_update(s, pair);
|
||||
}
|
||||
|
||||
gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m) { // returns true if it previously existsed
|
||||
rwlock_acquire_upgrade(m);
|
||||
if (type_set_exists(s, pair)) {
|
||||
rwlock_release_upgrade(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
rwlock_release_upgrade_and_acquire_write(m);
|
||||
defer (rwlock_release_write(m));
|
||||
|
||||
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_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m) { // returns true if it previously existsed
|
||||
TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)};
|
||||
return type_set_update_with_mutex(s, pair, m);
|
||||
}
|
||||
|
||||
|
||||
gb_internal Type *type_set_add(TypeSet *s, Type *ptr) {
|
||||
type_set_update(s, ptr);
|
||||
@@ -328,12 +373,20 @@ gb_internal u64 type_hash_canonical_type(Type *type) {
|
||||
if (type == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
u64 prev_hash = type->canonical_hash.load(std::memory_order_relaxed);
|
||||
if (prev_hash != 0) {
|
||||
return prev_hash;
|
||||
}
|
||||
|
||||
u64 hash = fnv64a(nullptr, 0);
|
||||
TypeWriter w = {};
|
||||
type_writer_make_hasher(&w, &hash);
|
||||
write_type_to_canonical_string(&w, type);
|
||||
hash = hash ? hash : 1;
|
||||
|
||||
return hash ? hash : 1;
|
||||
type->canonical_hash.store(hash, std::memory_order_relaxed);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
|
||||
|
||||
@@ -102,6 +102,8 @@ 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_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m);
|
||||
gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m);
|
||||
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);
|
||||
|
||||
+14
-1
@@ -1,5 +1,7 @@
|
||||
#include "parser_pos.cpp"
|
||||
|
||||
gb_global std::atomic<bool> g_parsing_done;
|
||||
|
||||
gb_internal bool in_vet_packages(AstFile *file) {
|
||||
if (file == nullptr) {
|
||||
return true;
|
||||
@@ -176,7 +178,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
|
||||
return nullptr;
|
||||
}
|
||||
if (f == nullptr) {
|
||||
f = node->thread_safe_file();
|
||||
if (g_parsing_done.load(std::memory_order_relaxed)) {
|
||||
f = node->file();
|
||||
} else {
|
||||
f = node->thread_safe_file();
|
||||
}
|
||||
}
|
||||
Ast *n = alloc_ast_node(f, node->kind);
|
||||
gb_memmove(n, node, ast_node_size(node->kind));
|
||||
@@ -744,6 +750,7 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token
|
||||
gb_internal Ast *ast_ident(AstFile *f, Token token) {
|
||||
Ast *result = alloc_ast_node(f, Ast_Ident);
|
||||
result->Ident.token = token;
|
||||
result->Ident.hash = string_hash(token.string);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -6592,6 +6599,10 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f)
|
||||
} else if (string_starts_with(lc, str_lit("vet"))) {
|
||||
f->vet_flags = parse_vet_tag(tok, lc);
|
||||
f->vet_flags_set = true;
|
||||
} else if (string_starts_with(lc, str_lit("test"))) {
|
||||
if ((build_context.command_kind & Command_test) == 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (string_starts_with(lc, str_lit("ignore"))) {
|
||||
return false;
|
||||
} else if (string_starts_with(lc, str_lit("private"))) {
|
||||
@@ -6962,6 +6973,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
}
|
||||
}
|
||||
|
||||
g_parsing_done.store(true, std::memory_order_relaxed);
|
||||
|
||||
return ParseFile_None;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,24 @@ enum AddressingMode : u8 {
|
||||
Addressing_SwizzleVariable = 14, // Swizzle indexed variable
|
||||
};
|
||||
|
||||
gb_global String const addressing_mode_strings[] = {
|
||||
str_lit("Invalid"),
|
||||
str_lit("NoValue"),
|
||||
str_lit("Value"),
|
||||
str_lit("Context"),
|
||||
str_lit("Variable"),
|
||||
str_lit("Constant"),
|
||||
str_lit("Type"),
|
||||
str_lit("Builtin"),
|
||||
str_lit("ProcGroup"),
|
||||
str_lit("MapIndex"),
|
||||
str_lit("OptionalOk"),
|
||||
str_lit("OptionalOkPtr"),
|
||||
str_lit("SoaVariable"),
|
||||
str_lit("SwizzleValue"),
|
||||
str_lit("SwizzleVariable"),
|
||||
};
|
||||
|
||||
struct TypeAndValue {
|
||||
Type * type;
|
||||
AddressingMode mode;
|
||||
@@ -396,6 +414,7 @@ struct AstSplitArgs {
|
||||
AST_KIND(Ident, "identifier", struct { \
|
||||
Token token; \
|
||||
Entity *entity; \
|
||||
u32 hash; \
|
||||
}) \
|
||||
AST_KIND(Implicit, "implicit", Token) \
|
||||
AST_KIND(Uninit, "uninitialized value", Token) \
|
||||
|
||||
+45
-3
@@ -16,6 +16,8 @@ template <typename T> gb_internal bool ptr_set_exists (PtrSet<T> *s, T ptr);
|
||||
template <typename T> gb_internal void ptr_set_remove (PtrSet<T> *s, T ptr);
|
||||
template <typename T> gb_internal void ptr_set_clear (PtrSet<T> *s);
|
||||
|
||||
#define FOR_PTR_SET(element, set_) for (auto *it = &(set_).keys[0], element = it ? *it : nullptr; (set_).keys != nullptr && it < &(set_).keys[(set_).capacity]; it++) if (element = *it, (*it != nullptr && *it != cast(void *)~(uintptr)(0ull)))
|
||||
|
||||
gb_internal gbAllocator ptr_set_allocator(void) {
|
||||
return heap_allocator();
|
||||
}
|
||||
@@ -83,7 +85,7 @@ gb_internal gb_inline void ptr_set_grow(PtrSet<T> *old_set) {
|
||||
PtrSet<T> new_set = {};
|
||||
ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
|
||||
|
||||
for (T ptr : *old_set) {
|
||||
FOR_PTR_SET(ptr, *old_set) {
|
||||
bool was_new = ptr_set_update(&new_set, ptr);
|
||||
GB_ASSERT(!was_new);
|
||||
}
|
||||
@@ -134,6 +136,44 @@ gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it pre
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal bool ptr_set_update_with_mutex(PtrSet<T> *s, T ptr, RWSpinLock *m) { // returns true if it previously existsed
|
||||
rwlock_acquire_upgrade(m);
|
||||
if (ptr_set_exists(s, ptr)) {
|
||||
rwlock_release_upgrade(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
rwlock_release_upgrade_and_acquire_write(m);
|
||||
defer (rwlock_release_write(m));
|
||||
|
||||
if (s->keys == nullptr) {
|
||||
ptr_set_init(s);
|
||||
} else if (ptr_set__full(s)) {
|
||||
ptr_set_grow(s);
|
||||
}
|
||||
GB_ASSERT(s->count < s->capacity);
|
||||
GB_ASSERT(s->capacity >= 0);
|
||||
|
||||
usize mask = s->capacity-1;
|
||||
u32 hash = ptr_map_hash_key(ptr);
|
||||
usize hash_index = (cast(usize)hash) & mask;
|
||||
GB_ASSERT(hash_index < s->capacity);
|
||||
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 == 0) {
|
||||
*key = ptr;
|
||||
s->count++;
|
||||
return false;
|
||||
}
|
||||
hash_index = (hash_index+1)&mask;
|
||||
}
|
||||
|
||||
GB_PANIC("ptr set out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T ptr_set_add(PtrSet<T> *s, T ptr) {
|
||||
ptr_set_update(s, ptr);
|
||||
@@ -157,7 +197,7 @@ gb_internal gb_inline void ptr_set_clear(PtrSet<T> *s) {
|
||||
gb_zero_size(s->keys, s->capacity*gb_size_of(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
/*template <typename T>
|
||||
struct PtrSetIterator {
|
||||
PtrSet<T> *set;
|
||||
usize index;
|
||||
@@ -201,4 +241,6 @@ gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
|
||||
template <typename T>
|
||||
gb_internal PtrSetIterator<T> end(PtrSet<T> &set) noexcept {
|
||||
return PtrSetIterator<T>{&set, set.capacity};
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@ gb_internal void mpsc_destroy(MPSCQueue<T> *q) {
|
||||
|
||||
template <typename T>
|
||||
gb_internal MPSCNode<T> *mpsc_alloc_node(MPSCQueue<T> *q, T const &value) {
|
||||
auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
|
||||
// auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
|
||||
auto new_node = gb_alloc_item(permanent_allocator(), MPSCNode<T>);
|
||||
new_node->value = value;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
+15
-10
@@ -633,23 +633,28 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
|
||||
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
|
||||
}
|
||||
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <iconv.h>
|
||||
#include <wchar.h>
|
||||
|
||||
gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
|
||||
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
|
||||
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
|
||||
iconv_close(conv);
|
||||
String string = copy_string(heap_allocator(), make_string(cast(u8 const*)multibyte_input, input_length)); /* Guarantee NULL terminator */
|
||||
u8* input = string.text;
|
||||
|
||||
return cast(int)result;
|
||||
mbstate_t ps = { 0 };
|
||||
size_t result = mbsrtowcs(output, cast(const char**)&input, output_size, &ps);
|
||||
|
||||
gb_free(heap_allocator(), string.text);
|
||||
return (result == (size_t)-1) ? -1 : (int)result;
|
||||
}
|
||||
|
||||
gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) {
|
||||
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
|
||||
size_t result = iconv(conv, cast(char**) &widechar_input, &input_length, cast(char **)&output, &output_size);
|
||||
iconv_close(conv);
|
||||
String string = copy_string(heap_allocator(), make_string(cast(u8 const*)widechar_input, input_length)); /* Guarantee NULL terminator */
|
||||
u8* input = string.text;
|
||||
|
||||
return cast(int)result;
|
||||
mbstate_t ps = { 0 };
|
||||
size_t result = wcsrtombs(output, cast(const wchar_t**)&input, output_size, &ps);
|
||||
|
||||
gb_free(heap_allocator(), string.text);
|
||||
return (result == (size_t)-1) ? -1 : (int)result;
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
|
||||
+14
-6
@@ -19,6 +19,11 @@ enum GrabState {
|
||||
Grab_Failed = 2,
|
||||
};
|
||||
|
||||
enum BroadcastWaitState {
|
||||
Nobody_Waiting = 0,
|
||||
Someone_Waiting = 1,
|
||||
};
|
||||
|
||||
struct ThreadPool {
|
||||
gbAllocator threads_allocator;
|
||||
Slice<Thread> threads;
|
||||
@@ -54,8 +59,8 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
|
||||
|
||||
for_array_off(i, 1, pool->threads) {
|
||||
Thread *t = &pool->threads[i];
|
||||
pool->tasks_available.fetch_add(1, std::memory_order_acquire);
|
||||
futex_broadcast(&pool->tasks_available);
|
||||
pool->tasks_available.store(Nobody_Waiting);
|
||||
futex_broadcast(&t->pool->tasks_available);
|
||||
thread_join_and_destroy(t);
|
||||
}
|
||||
|
||||
@@ -87,8 +92,10 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) {
|
||||
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||
|
||||
thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
|
||||
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
|
||||
futex_broadcast(&thread->pool->tasks_available);
|
||||
i32 state = Someone_Waiting;
|
||||
if (thread->pool->tasks_available.compare_exchange_strong(state, Nobody_Waiting)) {
|
||||
futex_broadcast(&thread->pool->tasks_available);
|
||||
}
|
||||
}
|
||||
|
||||
GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
|
||||
@@ -230,12 +237,13 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
|
||||
}
|
||||
|
||||
// if we've done all our work, and there's nothing to steal, go to sleep
|
||||
state = pool->tasks_available.load(std::memory_order_acquire);
|
||||
pool->tasks_available.store(Someone_Waiting);
|
||||
if (!pool->running) { break; }
|
||||
futex_wait(&pool->tasks_available, state);
|
||||
futex_wait(&pool->tasks_available, Someone_Waiting);
|
||||
|
||||
main_loop_continue:;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+48
-2
@@ -195,7 +195,13 @@ gb_internal void mutex_lock(RecursiveMutex *m) {
|
||||
// inside the lock
|
||||
return;
|
||||
}
|
||||
futex_wait(&m->owner, prev_owner);
|
||||
|
||||
// NOTE(lucas): we are doing spin lock since futex signal is expensive on OSX. The recursive locks are
|
||||
// very short lived so we don't hit this mega often and I see no perform regression on windows (with
|
||||
// a performance uplift on OSX).
|
||||
|
||||
//futex_wait(&m->owner, prev_owner);
|
||||
yield_thread();
|
||||
}
|
||||
}
|
||||
gb_internal bool mutex_try_lock(RecursiveMutex *m) {
|
||||
@@ -216,7 +222,9 @@ gb_internal void mutex_unlock(RecursiveMutex *m) {
|
||||
return;
|
||||
}
|
||||
m->owner.exchange(0, std::memory_order_release);
|
||||
futex_signal(&m->owner);
|
||||
// NOTE(lucas): see comment about spin lock in mutex_lock above
|
||||
|
||||
// futex_signal(&m->owner);
|
||||
// outside the lock
|
||||
}
|
||||
|
||||
@@ -448,6 +456,44 @@ gb_internal void semaphore_wait(Semaphore *s) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static const int RWLOCK_WRITER = 1<<0;
|
||||
static const int RWLOCK_UPGRADED = 1<<1;
|
||||
static const int RWLOCK_READER = 1<<2;
|
||||
struct RWSpinLock {
|
||||
Futex bits;
|
||||
};
|
||||
|
||||
void rwlock_release_write(RWSpinLock *l) {
|
||||
l->bits.fetch_and(~(RWLOCK_WRITER | RWLOCK_UPGRADED), std::memory_order_release);
|
||||
futex_signal(&l->bits);
|
||||
}
|
||||
|
||||
bool rwlock_try_acquire_upgrade(RWSpinLock *l) {
|
||||
int value = l->bits.fetch_or(RWLOCK_UPGRADED, std::memory_order_acquire);
|
||||
return (value & (RWLOCK_UPGRADED | RWLOCK_WRITER)) == 0;
|
||||
}
|
||||
|
||||
void rwlock_acquire_upgrade(RWSpinLock *l) {
|
||||
while (!rwlock_try_acquire_upgrade(l)) {
|
||||
futex_wait(&l->bits, RWLOCK_UPGRADED | RWLOCK_WRITER);
|
||||
}
|
||||
}
|
||||
void rwlock_release_upgrade(RWSpinLock *l) {
|
||||
l->bits.fetch_add(-RWLOCK_UPGRADED, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) {
|
||||
int expect = RWLOCK_UPGRADED;
|
||||
return l->bits.compare_exchange_strong(expect, RWLOCK_WRITER, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) {
|
||||
while (!rwlock_try_release_upgrade_and_acquire_write(l)) {
|
||||
futex_wait(&l->bits, RWLOCK_UPGRADED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Parker {
|
||||
Futex state;
|
||||
};
|
||||
|
||||
+106
-23
@@ -206,13 +206,18 @@ struct TypeProc {
|
||||
bool optional_ok;
|
||||
};
|
||||
|
||||
struct TypeNamed {
|
||||
String name;
|
||||
Type * base;
|
||||
Entity *type_name; /* Entity_TypeName */
|
||||
|
||||
BlockingMutex gen_types_data_mutex;
|
||||
GenTypesData *gen_types_data;
|
||||
};
|
||||
|
||||
#define TYPE_KINDS \
|
||||
TYPE_KIND(Basic, BasicType) \
|
||||
TYPE_KIND(Named, struct { \
|
||||
String name; \
|
||||
Type * base; \
|
||||
Entity *type_name; /* Entity_TypeName */ \
|
||||
}) \
|
||||
TYPE_KIND(Named, TypeNamed) \
|
||||
TYPE_KIND(Generic, struct { \
|
||||
i64 id; \
|
||||
String name; \
|
||||
@@ -334,6 +339,7 @@ struct Type {
|
||||
// NOTE(bill): These need to be at the end to not affect the unionized data
|
||||
std::atomic<i64> cached_size;
|
||||
std::atomic<i64> cached_align;
|
||||
std::atomic<u64> canonical_hash;
|
||||
std::atomic<u32> flags; // TypeFlag
|
||||
bool failure;
|
||||
};
|
||||
@@ -429,11 +435,8 @@ gb_internal Selection make_selection(Entity *entity, Array<i32> index, bool indi
|
||||
}
|
||||
|
||||
gb_internal void selection_add_index(Selection *s, isize index) {
|
||||
// IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form
|
||||
// of heap allocation
|
||||
// TODO(bill): Find a way to use a backing buffer for initial use as the general case is probably .count<3
|
||||
if (s->index.data == nullptr) {
|
||||
array_init(&s->index, heap_allocator());
|
||||
array_init(&s->index, permanent_allocator());
|
||||
}
|
||||
array_add(&s->index, cast(i32)index);
|
||||
}
|
||||
@@ -441,7 +444,7 @@ gb_internal void selection_add_index(Selection *s, isize index) {
|
||||
gb_internal Selection selection_combine(Selection const &lhs, Selection const &rhs) {
|
||||
Selection new_sel = lhs;
|
||||
new_sel.indirect = lhs.indirect || rhs.indirect;
|
||||
new_sel.index = array_make<i32>(heap_allocator(), lhs.index.count+rhs.index.count);
|
||||
new_sel.index = array_make<i32>(permanent_allocator(), lhs.index.count+rhs.index.count);
|
||||
array_copy(&new_sel.index, lhs.index, 0);
|
||||
array_copy(&new_sel.index, rhs.index, lhs.index.count);
|
||||
return new_sel;
|
||||
@@ -1230,7 +1233,6 @@ gb_internal bool is_type_named(Type *t) {
|
||||
}
|
||||
|
||||
gb_internal bool is_type_boolean(Type *t) {
|
||||
// t = core_type(t);
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -1239,7 +1241,6 @@ gb_internal bool is_type_boolean(Type *t) {
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_integer(Type *t) {
|
||||
// t = core_type(t);
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -1249,6 +1250,7 @@ gb_internal bool is_type_integer(Type *t) {
|
||||
}
|
||||
gb_internal bool is_type_integer_like(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
|
||||
}
|
||||
@@ -1281,7 +1283,6 @@ gb_internal bool is_type_integer_128bit(Type *t) {
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_rune(Type *t) {
|
||||
// t = core_type(t);
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -1290,7 +1291,6 @@ gb_internal bool is_type_rune(Type *t) {
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_numeric(Type *t) {
|
||||
// t = core_type(t);
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -1377,14 +1377,20 @@ gb_internal bool is_type_ordered_numeric(Type *t) {
|
||||
gb_internal bool is_type_constant_type(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_typeid) {
|
||||
return true;
|
||||
}
|
||||
return (t->Basic.flags & BasicFlag_ConstantType) != 0;
|
||||
}
|
||||
if (t->kind == Type_BitSet) {
|
||||
case Type_BitSet:
|
||||
return true;
|
||||
}
|
||||
if (t->kind == Type_Proc) {
|
||||
case Type_Proc:
|
||||
return true;
|
||||
case Type_Array:
|
||||
return is_type_constant_type(t->Array.elem);
|
||||
case Type_EnumeratedArray:
|
||||
return is_type_constant_type(t->EnumeratedArray.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1448,24 +1454,28 @@ gb_internal bool is_type_tuple(Type *t) {
|
||||
return t->kind == Type_Tuple;
|
||||
}
|
||||
gb_internal bool is_type_uintptr(Type *t) {
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.kind == Basic_uintptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_rawptr(Type *t) {
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_rawptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u8(Type *t) {
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_u8;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u16(Type *t) {
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_u16;
|
||||
}
|
||||
@@ -1612,6 +1622,7 @@ gb_internal bool is_matrix_square(Type *t) {
|
||||
|
||||
gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (is_type_integer(t)) {
|
||||
return true;
|
||||
} else if (is_type_float(t)) {
|
||||
@@ -1839,40 +1850,49 @@ gb_internal Type *base_complex_elem_type(Type *t) {
|
||||
|
||||
gb_internal bool is_type_struct(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return t->kind == Type_Struct;
|
||||
}
|
||||
gb_internal bool is_type_union(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return t->kind == Type_Union;
|
||||
}
|
||||
gb_internal bool is_type_soa_struct(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_raw_union(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_Struct && t->Struct.is_raw_union);
|
||||
}
|
||||
gb_internal bool is_type_enum(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_Enum);
|
||||
}
|
||||
gb_internal bool is_type_bit_set(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_BitSet);
|
||||
}
|
||||
gb_internal bool is_type_bit_field(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_BitField);
|
||||
}
|
||||
gb_internal bool is_type_map(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return t->kind == Type_Map;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_union_maybe_pointer(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Union && t->Union.variants.count == 1) {
|
||||
Type *v = t->Union.variants[0];
|
||||
return is_type_internally_pointer_like(v);
|
||||
@@ -1883,6 +1903,7 @@ gb_internal bool is_type_union_maybe_pointer(Type *t) {
|
||||
|
||||
gb_internal bool is_type_union_maybe_pointer_original_alignment(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Union && t->Union.variants.count == 1) {
|
||||
Type *v = t->Union.variants[0];
|
||||
if (is_type_internally_pointer_like(v)) {
|
||||
@@ -1917,6 +1938,7 @@ gb_internal TypeEndianKind type_endian_kind_of(Type *t) {
|
||||
|
||||
gb_internal bool is_type_endian_big(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
if (t->Basic.flags & BasicFlag_EndianBig) {
|
||||
return true;
|
||||
@@ -1933,6 +1955,7 @@ gb_internal bool is_type_endian_big(Type *t) {
|
||||
}
|
||||
gb_internal bool is_type_endian_little(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
if (t->Basic.flags & BasicFlag_EndianLittle) {
|
||||
return true;
|
||||
@@ -1950,6 +1973,7 @@ gb_internal bool is_type_endian_little(Type *t) {
|
||||
|
||||
gb_internal bool is_type_endian_platform(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) == 0;
|
||||
} else if (t->kind == Type_BitSet) {
|
||||
@@ -1965,6 +1989,7 @@ gb_internal bool types_have_same_internal_endian(Type *a, Type *b) {
|
||||
}
|
||||
gb_internal bool is_type_endian_specific(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_BitSet) {
|
||||
t = bit_set_to_int(t);
|
||||
}
|
||||
@@ -2062,19 +2087,23 @@ gb_internal Type *integer_endian_type_to_platform_type(Type *t) {
|
||||
|
||||
gb_internal bool is_type_any(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
|
||||
}
|
||||
gb_internal bool is_type_typeid(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
return (t->kind == Type_Basic && t->Basic.kind == Basic_typeid);
|
||||
}
|
||||
gb_internal bool is_type_untyped_nil(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
|
||||
return (t->kind == Type_Basic && (t->Basic.kind == Basic_UntypedNil || t->Basic.kind == Basic_UntypedUninit));
|
||||
}
|
||||
gb_internal bool is_type_untyped_uninit(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
|
||||
return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUninit);
|
||||
}
|
||||
@@ -2484,18 +2513,70 @@ gb_internal bool type_has_nil(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_union_constantable(Type *type) {
|
||||
Type *bt = base_type(type);
|
||||
GB_ASSERT(bt->kind == Type_Union);
|
||||
|
||||
if (bt->Union.variants.count == 0) {
|
||||
return true;
|
||||
} else if (bt->Union.variants.count == 1) {
|
||||
return is_type_constant_type(bt->Union.variants[0]);
|
||||
}
|
||||
|
||||
for (Type *v : bt->Union.variants) {
|
||||
if (!is_type_constant_type(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_raw_union_constantable(Type *type) {
|
||||
Type *bt = base_type(type);
|
||||
GB_ASSERT(bt->kind == Type_Struct);
|
||||
GB_ASSERT(bt->Struct.is_raw_union);
|
||||
|
||||
for (Entity *f : bt->Struct.fields) {
|
||||
if (!is_type_constant_type(f->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// return true;
|
||||
return false; // Disable raw union constants for the time being
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool elem_type_can_be_constant(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == t_invalid) {
|
||||
return false;
|
||||
}
|
||||
if (is_type_any(t) || is_type_union(t) || is_type_raw_union(t)) {
|
||||
if (is_type_any(t)) {
|
||||
return false;
|
||||
}
|
||||
if (is_type_raw_union(t)) {
|
||||
return is_type_raw_union_constantable(t);
|
||||
}
|
||||
if (is_type_union(t)) {
|
||||
return is_type_union_constantable(t);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal bool elem_cannot_be_constant(Type *t) {
|
||||
if (is_type_any(t)) {
|
||||
return true;
|
||||
}
|
||||
if (is_type_union(t)) {
|
||||
return !is_type_union_constantable(t);
|
||||
}
|
||||
if (is_type_raw_union(t)) {
|
||||
return !is_type_raw_union_constantable(t);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_type_lock_free(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t == t_invalid) {
|
||||
@@ -2837,6 +2918,7 @@ gb_internal bool are_types_identical(Type *x, Type *y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// MUTEX_GUARD(&g_type_mutex);
|
||||
return are_types_identical_internal(x, y, false);
|
||||
}
|
||||
gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
|
||||
@@ -2864,6 +2946,7 @@ gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// MUTEX_GUARD(&g_type_mutex);
|
||||
return are_types_identical_internal(x, y, true);
|
||||
}
|
||||
|
||||
@@ -3932,7 +4015,7 @@ gb_internal i64 type_size_of(Type *t) {
|
||||
TypePath path{};
|
||||
type_path_init(&path);
|
||||
{
|
||||
MUTEX_GUARD(&g_type_mutex);
|
||||
// MUTEX_GUARD(&g_type_mutex);
|
||||
size = type_size_of_internal(t, &path);
|
||||
t->cached_size.store(size);
|
||||
}
|
||||
@@ -3952,7 +4035,7 @@ gb_internal i64 type_align_of(Type *t) {
|
||||
TypePath path{};
|
||||
type_path_init(&path);
|
||||
{
|
||||
MUTEX_GUARD(&g_type_mutex);
|
||||
// MUTEX_GUARD(&g_type_mutex);
|
||||
t->cached_align.store(type_align_of_internal(t, &path));
|
||||
}
|
||||
type_path_free(&path);
|
||||
@@ -4678,7 +4761,7 @@ gb_internal Type *alloc_type_tuple_from_field_types(Type **field_types, isize fi
|
||||
}
|
||||
|
||||
Type *t = alloc_type_tuple();
|
||||
t->Tuple.variables = slice_make<Entity *>(heap_allocator(), field_count);
|
||||
t->Tuple.variables = slice_make<Entity *>(permanent_allocator(), field_count);
|
||||
|
||||
Scope *scope = nullptr;
|
||||
for_array(i, t->Tuple.variables) {
|
||||
|
||||
Reference in New Issue
Block a user