aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Gavin <danielgavin5@hotmail.com>2021-04-23 10:24:05 +0200
committerDaniel Gavin <danielgavin5@hotmail.com>2021-04-23 10:24:05 +0200
commitf10f7ebbf1c9833c74d09db68c0a0f5a149bde8d (patch)
treed25d97bafc0f762e537428f99607680aa5e434b3 /src
parent40ed7e48d0e4a1f000efbd03d19a4eebe9b8e2f6 (diff)
parent17bbb48d8a04aaf6cc53777fe4da6ba1b7fff61b (diff)
Merge remote-tracking branch 'upstream/master' into prototype-fmt
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp5
-rw-r--r--src/check_decl.cpp54
-rw-r--r--src/check_expr.cpp490
-rw-r--r--src/check_stmt.cpp20
-rw-r--r--src/check_type.cpp42
-rw-r--r--src/checker.cpp60
-rw-r--r--src/checker.hpp6
-rw-r--r--src/checker_builtin_procs.hpp29
-rw-r--r--src/docs_format.cpp4
-rw-r--r--src/docs_writer.cpp10
-rw-r--r--src/entity.cpp66
-rw-r--r--src/gb/gb.h4
-rw-r--r--src/ir.cpp13
-rw-r--r--src/ir_print.cpp10
-rw-r--r--src/llvm_backend.cpp466
-rw-r--r--src/llvm_backend.hpp3
-rw-r--r--src/llvm_backend_opt.cpp69
-rw-r--r--src/main.cpp51
-rw-r--r--src/parser.cpp1
-rw-r--r--src/parser.hpp2
-rw-r--r--src/types.cpp45
21 files changed, 1089 insertions, 361 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 8df045a82..92ac5d284 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -104,6 +104,7 @@ enum BuildModeKind {
BuildMode_DynamicLibrary,
BuildMode_Object,
BuildMode_Assembly,
+ BuildMode_LLVM_IR,
};
enum CommandKind : u32 {
@@ -113,7 +114,7 @@ enum CommandKind : u32 {
Command_query = 1<<4,
Command_doc = 1<<5,
Command_version = 1<<6,
- Command_test = 1<<7,
+ Command_test = 1<<7,
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
Command__does_build = Command_run|Command_build|Command_test,
@@ -838,7 +839,7 @@ void init_build_context(TargetMetrics *cross_target) {
bc->link_flags = str_lit("-arch arm64 ");
break;
}
- if (!bc->use_llvm_api) {
+ if ((bc->command_kind & Command__does_build) != 0 && !bc->use_llvm_api) {
gb_printf_err("The arm64 architecture is only supported with -llvm-api\n");;
gb_exit(1);
}
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 51c0b6ee5..85c58fdf9 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -343,17 +343,31 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
return;
}
- // IMPORTANT TODO(bill)
- // Date: 2018-09-29
- // This assert fails on `using import` if the name of the alias is the same. What should be the expected behaviour?
- // Namespace collision or override? Overridding is the current behaviour
+ // 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
+ // relying on "constant" data such as the Entity.type and Entity.Constant.value
//
- // using import "foo"
- // bar :: foo.bar;
-
- // GB_ASSERT_MSG(found_entity == original_entity, "%.*s == %.*s", LIT(found_entity->token.string), LIT(new_entity->token.string));
+ // 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
string_map_set(&found_scope->elements, original_name, new_entity);
+
+ original_entity->type = new_entity->type;
+
+ if (original_entity->identifier == nullptr) {
+ original_entity->identifier = new_entity->identifier;
+ }
+ if (original_entity->identifier != nullptr &&
+ original_entity->identifier->kind == Ast_Ident) {
+ original_entity->identifier->Ident.entity = new_entity;
+ }
+ original_entity->flags |= EntityFlag_Overridden;
+
+ // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
+ // This is most likely NEVER required, but it does not at all hurt to keep
+ isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
+ isize size = gb_size_of(*original_entity) - offset;
+ gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
}
@@ -374,6 +388,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
Operand operand = {};
+ Entity *other_entity = nullptr;
if (init != nullptr) {
Entity *entity = nullptr;
if (init->kind == Ast_Ident) {
@@ -412,7 +427,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
GB_ASSERT(operand.proc_group->kind == Entity_ProcGroup);
// NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope
// Thank goodness I made entities a tagged union to allow for this implace patching
- // override_entity_in_scope(e, operand.proc_group);
e->kind = Entity_ProcGroup;
e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities);
return;
@@ -454,7 +468,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
}
}
-
return;
}
}
@@ -694,6 +707,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->flags |= EntityFlag_Cold;
}
+ e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
+
+
+ switch (e->Procedure.optimization_mode) {
+ case ProcedureOptimizationMode_None:
+ case ProcedureOptimizationMode_Minimal:
+ if (pl->inlining == ProcInlining_inline) {
+ error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\"");
+ }
+ break;
+ }
+
e->Procedure.is_export = ac.is_export;
e->deprecated_message = ac.deprecated_message;
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
@@ -718,11 +743,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
gb_string_free(str);
}
- if (pt->calling_convention != ProcCC_Odin &&
- pt->calling_convention != ProcCC_Contextless) {
+ if (pt->calling_convention != ProcCC_Odin) {
error(e->token, "Procedure 'main' cannot have a custom calling convention");
}
- pt->calling_convention = ProcCC_Contextless;
+ pt->calling_convention = ProcCC_Odin;
if (e->pkg->kind == Package_Init) {
if (ctx->info->entry_point != nullptr) {
error(e->token, "Redeclaration of the entry pointer procedure 'main'");
@@ -846,7 +870,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
-void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
+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);
@@ -946,7 +970,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr,
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
}
-void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
+void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
auto *pge = &pg_entity->ProcGroup;
String proc_group_name = pg_entity->token.string;
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 2114746a3..9818b5015 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -79,15 +79,12 @@ void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e,
Type * check_type (CheckerContext *c, Ast *expression);
Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
Type * make_optional_ok_type (Type *value, bool typed=true);
-void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
void check_not_tuple (CheckerContext *c, Operand *operand);
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
gbString expr_to_string (Ast *expression);
-void check_entity_decl (CheckerContext *c, Entity *e, DeclInfo *decl, Type *named_type);
-void check_const_decl (CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
bool check_is_terminating (Ast *node, String const &label);
@@ -654,13 +651,27 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
Ast *expr = unparen_expr(operand->expr);
- if (expr != nullptr && expr->kind == Ast_AutoCast) {
- Operand x = *operand;
- x.expr = expr->AutoCast.expr;
- bool ok = check_cast_internal(c, &x, type);
- if (ok) {
- return MAXIMUM_TYPE_DISTANCE;
- }
+ if (expr != nullptr) {
+ if (expr->kind == Ast_AutoCast) {
+ Operand x = *operand;
+ x.expr = expr->AutoCast.expr;
+ bool ok = check_cast_internal(c, &x, type);
+ if (ok) {
+ return MAXIMUM_TYPE_DISTANCE;
+ }
+ } /*else if (expr->kind == Ast_CallExpr) {
+ // NOTE(bill, 2021-04-19): Allow assignment of procedure calls with #optional_ok
+ ast_node(ce, CallExpr, expr);
+ Type *pt = base_type(type_of_expr(ce->proc));
+ if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
+ Operand x = *operand;
+ x.type = pt->Proc.results->Tuple.variables[0]->type;
+ i64 res = check_distance_between_types(c, &x, type);
+ if (res >= 0) {
+ return res+1;
+ }
+ }
+ }*/
}
return -1;
@@ -774,6 +785,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
LIT(context_name));
operand->mode = Addressing_Invalid;
}
+
+
return;
}
@@ -1714,12 +1727,14 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
}
-void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
+void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
GB_ASSERT(o->mode == Addressing_Constant);
- if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) {
+ if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
+ gbString c = type_to_string(o->type);
defer(
+ gb_string_free(c);
gb_string_free(b);
gb_string_free(a);
o->mode = Addressing_Invalid;
@@ -1729,12 +1744,12 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
} else {
- error(o->expr, "Cannot convert '%s' to '%s'", a, b);
- check_assignment_error_suggestion(c, o, type);
+ error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
+ check_assignment_error_suggestion(ctx, o, type);
}
} else {
- error(o->expr, "Cannot convert '%s' to '%s'", a, b);
- check_assignment_error_suggestion(c, o, type);
+ error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
+ check_assignment_error_suggestion(ctx, o, type);
}
}
}
@@ -2224,6 +2239,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return true;
}
+ // if (is_type_tuple(src)) {
+ // Ast *expr = unparen_expr(operand->expr);
+ // if (expr && expr->kind == Ast_CallExpr) {
+ // // NOTE(bill, 2021-04-19): Allow casting procedure calls with #optional_ok
+ // ast_node(ce, CallExpr, expr);
+ // Type *pt = base_type(type_of_expr(ce->proc));
+ // if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
+ // if (pt->Proc.result_count > 0) {
+ // Operand op = *operand;
+ // op.type = pt->Proc.results->Tuple.variables[0]->type;
+ // bool ok = check_is_castable_to(c, &op, y);
+ // if (ok) {
+ // ce->optional_ok_one = true;
+ // }
+ // return ok;
+ // }
+ // }
+ // }
+ // }
+
if (is_constant && is_type_untyped(src) && is_type_string(src)) {
if (is_type_u8_array(dst)) {
String s = operand->value.value_string;
@@ -2339,6 +2374,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
if (is_type_rawptr(src) && is_type_proc(dst)) {
return true;
}
+
return false;
}
@@ -2728,31 +2764,27 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
ExactValue a = x->value;
ExactValue b = y->value;
- // Type *type = base_type(x->type);
- Type *type = x->type;
- if (is_type_pointer(type)) {
- GB_ASSERT(op.kind == Token_Sub);
- i64 bytes = a.value_pointer - b.value_pointer;
- i64 diff = bytes/type_size_of(type);
- x->value = exact_value_pointer(diff);
- return;
- }
-
- if (!is_type_constant_type(type)) {
+ if (!is_type_constant_type(x->type)) {
+ #if 0
gbString xt = type_to_string(x->type);
gbString err_str = expr_to_string(node);
error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str);
gb_string_free(err_str);
gb_string_free(xt);
x->mode = Addressing_Invalid;
+ #else
+ // NOTE(bill, 2021-04-21): The above is literally a useless error message.
+ // Why did I add it in the first place?!
+ x->mode = Addressing_Value;
+ #endif
return;
}
- if (op.kind == Token_Quo && is_type_integer(type)) {
+ if (op.kind == Token_Quo && is_type_integer(x->type)) {
op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
}
- if (is_type_bit_set(type)) {
+ if (is_type_bit_set(x->type)) {
switch (op.kind) {
case Token_Add: op.kind = Token_Or; break;
case Token_Sub: op.kind = Token_AndNot; break;
@@ -2761,11 +2793,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
x->value = exact_binary_operator_value(op.kind, a, b);
- if (is_type_typed(type)) {
+ if (is_type_typed(x->type)) {
if (node != nullptr) {
x->expr = node;
}
- check_is_expressible(c, x, type);
+ check_is_expressible(c, x, x->type);
}
return;
} else if (is_type_string(x->type)) {
@@ -2797,8 +2829,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
+ GB_ASSERT(e != nullptr);
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
if (found == nullptr) {
+ if (type != nullptr && type != t_invalid) {
+ if (e->tav.type == nullptr || e->tav.type == t_invalid) {
+ add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
+ }
+ }
return;
}
ExprInfo old = *found;
@@ -2865,6 +2903,7 @@ void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
gbString expr_str = expr_to_string(operand->expr);
gbString type_str = type_to_string(target_type);
+ gbString from_type_str = type_to_string(operand->type);
char const *extra_text = "";
if (operand->mode == Addressing_Constant) {
@@ -2875,8 +2914,9 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
}
}
}
- error(operand->expr, "Cannot convert '%s' to '%s'%s", expr_str, type_str, extra_text);
+ error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
+ gb_string_free(from_type_str);
gb_string_free(type_str);
gb_string_free(expr_str);
operand->mode = Addressing_Invalid;
@@ -5717,8 +5757,131 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_NoValue;
break;
+ case BuiltinProc_trap:
+ case BuiltinProc_debug_trap:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ }
+ operand->mode = Addressing_NoValue;
+ break;
+
+ case BuiltinProc_read_cycle_counter:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ }
+ operand->mode = Addressing_Value;
+ operand->type = t_i64;
+ break;
+
+ case BuiltinProc_count_ones:
+ case BuiltinProc_trailing_zeros:
+ case BuiltinProc_reverse_bits:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ // continue anyway
+ }
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+
+ if (!is_type_integer_like(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ } else if (x.type == t_llvm_bool) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = default_type(x.type);
+ }
+ break;
+
+ case BuiltinProc_byte_swap:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ // continue anyway
+ }
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+
+ if (!is_type_integer_like(x.type) && !is_type_float(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ } else if (x.type == t_llvm_bool) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ }
+ i64 sz = type_size_of(x.type);
+ if (sz < 2) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz);
+ gb_string_free(xts);
+ }
+ operand->mode = Addressing_Value;
+ operand->type = default_type(x.type);
+ }
+ break;
+
+ case BuiltinProc_overflow_add:
+ case BuiltinProc_overflow_sub:
+ case BuiltinProc_overflow_mul:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ // continue anyway
+ }
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]);
+ check_expr(c, &y, ce->args[1]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (y.mode == Addressing_Invalid) {
+ return false;
+ }
+ convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &x, y.type);
+ if (is_type_untyped(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ if (!is_type_integer(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ Type *ct = core_type(x.type);
+ if (is_type_different_to_arch_endianness(ct)) {
+ GB_ASSERT(ct->kind == Type_Basic);
+ if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ }
+ operand->mode = Addressing_Value;
+ operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional
+ }
+ break;
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
@@ -5859,8 +6022,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_fixed_point_div_sat:
{
if (!build_context.use_llvm_api) {
- error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name));
- return false;
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ // continue anyway
}
Operand x = {};
@@ -5885,7 +6048,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (!are_types_identical(x.type, y.type)) {
gbString xts = type_to_string(x.type);
gbString yts = type_to_string(y.type);
- error(x.expr, "Mismatched types for %.*s, %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+ error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
gb_string_free(yts);
gb_string_free(xts);
return false;
@@ -5893,7 +6056,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (!is_type_integer(x.type) || is_type_untyped(x.type)) {
gbString xts = type_to_string(x.type);
- error(x.expr, "Expected an integer type for %.*s, got %s", LIT(builtin_procs[id].name), xts);
+ error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
gb_string_free(xts);
return false;
}
@@ -5903,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (z.mode != Addressing_Constant || !is_type_integer(z.type)) {
- error(z.expr, "Expected a constant integer for the scale in %.*s", LIT(builtin_procs[id].name));
+ error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name));
return false;
}
i64 n = exact_value_to_i64(z.value);
if (n <= 0) {
- error(z.expr, "Scale parameter in %.*s must be positive, got %lld", LIT(builtin_procs[id].name), n);
+ error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n);
return false;
}
i64 sz = 8*type_size_of(x.type);
if (n > sz) {
- error(z.expr, "Scale parameter in %.*s is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
+ error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
return false;
}
@@ -5923,6 +6086,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
+ case BuiltinProc_expect:
+ if (!build_context.use_llvm_api) {
+ error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+ // continue anyway
+ }
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]);
+ check_expr(c, &y, ce->args[1]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (y.mode == Addressing_Invalid) {
+ return false;
+ }
+ convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &x, y.type);
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xts = type_to_string(x.type);
+ gbString yts = type_to_string(y.type);
+ error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+ gb_string_free(yts);
+ gb_string_free(xts);
+ *operand = x; // minimize error propagation
+ return true;
+ }
+
+ if (!is_type_integer_like(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+ gb_string_free(xts);
+ *operand = x;
+ return true;
+ }
+
+ if (y.mode != Addressing_Constant) {
+ error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name));
+ }
+
+ if (x.mode == Addressing_Constant) {
+ // NOTE(bill): just completely ignore this intrinsic entirely
+ *operand = x;
+ return true;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ }
+ break;
+
+
+
case BuiltinProc_type_base_type:
if (operand->mode != Addressing_Type) {
@@ -6462,17 +6678,38 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
if (o.type == nullptr || o.type->kind != Type_Tuple) {
if (lhs.count == 2 && rhs.count == 1 &&
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
- Type *tuple = make_optional_ok_type(o.type);
- add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+ bool do_normal = true;
+ Ast *expr = unparen_expr(o.expr);
- Operand val = o;
- Operand ok = o;
- val.mode = Addressing_Value;
- ok.mode = Addressing_Value;
- ok.type = t_untyped_bool;
- array_add(operands, val);
- array_add(operands, ok);
+ Operand val0 = o;
+ Operand val1 = o;
+ val0.mode = Addressing_Value;
+ val1.mode = Addressing_Value;
+ val1.type = t_untyped_bool;
+
+ if (expr->kind == Ast_CallExpr) {
+ Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
+ if (is_type_proc(pt)) {
+ do_normal = false;
+ Type *tuple = pt->Proc.results;
+ add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+
+ if (pt->Proc.result_count >= 2) {
+ Type *t1 = tuple->Tuple.variables[1]->type;
+ val1.type = t1;
+ }
+ expr->CallExpr.optional_ok_one = false;
+ }
+ }
+
+ if (do_normal) {
+ Type *tuple = make_optional_ok_type(o.type);
+ add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+ }
+
+ array_add(operands, val0);
+ array_add(operands, val1);
optional_ok = true;
tuple_index += 2;
} else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) {
@@ -6493,27 +6730,12 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
}
} else {
TypeTuple *tuple = &o.type->Tuple;
- if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) {
- GB_ASSERT(tuple->variables.count == 2);
- Ast *expr = unparen_expr(o.expr);
- if (expr->kind == Ast_CallExpr) {
- expr->CallExpr.optional_ok_one = true;
- }
- Operand val = o;
- val.type = tuple->variables[0]->type;
- val.mode = Addressing_Value;
- array_add(operands, val);
- tuple_index += tuple->variables.count;
-
- add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
- } else {
- for_array(j, tuple->variables) {
- o.type = tuple->variables[j]->type;
- array_add(operands, o);
- }
-
- tuple_index += tuple->variables.count;
+ for_array(j, tuple->variables) {
+ o.type = tuple->variables[j]->type;
+ array_add(operands, o);
}
+
+ tuple_index += tuple->variables.count;
}
}
@@ -6570,18 +6792,38 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
if (o.type == nullptr || o.type->kind != Type_Tuple) {
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
- Type *tuple = make_optional_ok_type(o.type);
- add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+ bool do_normal = true;
+ Ast *expr = unparen_expr(o.expr);
- Operand val = o;
- Operand ok = o;
- val.mode = Addressing_Value;
- ok.mode = Addressing_Value;
- // ok.type = t_bool;
- ok.type = t_untyped_bool;
- array_add(operands, val);
- array_add(operands, ok);
+ Operand val0 = o;
+ Operand val1 = o;
+ val0.mode = Addressing_Value;
+ val1.mode = Addressing_Value;
+ val1.type = t_untyped_bool;
+
+ if (expr->kind == Ast_CallExpr) {
+ Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
+ if (is_type_proc(pt)) {
+ do_normal = false;
+ Type *tuple = pt->Proc.results;
+ add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+
+ if (pt->Proc.result_count >= 2) {
+ Type *t1 = tuple->Tuple.variables[1]->type;
+ val1.type = t1;
+ }
+ expr->CallExpr.optional_ok_one = false;
+ }
+ }
+
+ if (do_normal) {
+ Type *tuple = make_optional_ok_type(o.type);
+ add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+ }
+
+ array_add(operands, val0);
+ array_add(operands, val1);
optional_ok = true;
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
} else {
@@ -6590,30 +6832,13 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
}
} else {
TypeTuple *tuple = &o.type->Tuple;
- if (o.mode == Addressing_OptionalOk && lhs_count == 1) {
- GB_ASSERT(tuple->variables.count == 2);
- Ast *expr = unparen_expr(o.expr);
- if (expr->kind == Ast_CallExpr) {
- expr->CallExpr.optional_ok_one = true;
- }
- Operand val = o;
- val.type = tuple->variables[0]->type;
- val.mode = Addressing_Value;
- array_add(operands, val);
-
- isize count = tuple->variables.count;
- tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
-
- add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
- } else {
- for_array(j, tuple->variables) {
- o.type = tuple->variables[j]->type;
- array_add(operands, o);
- }
-
- isize count = tuple->variables.count;
- tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
+ for_array(j, tuple->variables) {
+ o.type = tuple->variables[j]->type;
+ array_add(operands, o);
}
+
+ isize count = tuple->variables.count;
+ tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
}
}
@@ -6863,6 +7088,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
data->score = score;
data->result_type = final_proc_type->Proc.results;
data->gen_entity = gen_entity;
+ add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {});
}
return err;
@@ -7080,6 +7306,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
data->score = score;
data->result_type = pt->results;
data->gen_entity = gen_entity;
+ add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {});
}
return err;
@@ -7333,7 +7560,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
-
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
if (err != CallArgumentError_None) {
@@ -7341,7 +7567,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
}
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
-
+ if (entity_to_use != nullptr) {
+ update_expr_type(c, operand->expr, entity_to_use->type, true);
+ }
return data;
}
@@ -7613,6 +7841,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
+ if (entity_to_use != nullptr) {
+ update_expr_type(c, operand->expr, entity_to_use->type, true);
+ }
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
@@ -7628,7 +7859,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
}
-
return data;
}
} else {
@@ -7644,6 +7874,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
+ if (entity_to_use != nullptr) {
+ update_expr_type(c, operand->expr, entity_to_use->type, true);
+ }
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
@@ -7659,10 +7892,10 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
}
-
return data;
}
+
CallArgumentData data = {};
data.result_type = t_invalid;
return data;
@@ -8147,6 +8380,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
Type *pt = base_type(proc_type);
+ if (pt == t_invalid) {
+ if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+ pt = type_of_expr(operand->expr->CallExpr.proc);
+ }
+ if (pt == t_invalid && data.gen_entity) {
+ pt = data.gen_entity->type;
+ }
+ }
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
@@ -8175,7 +8416,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
switch (inlining) {
- case ProcInlining_inline: {
+ case ProcInlining_inline:
if (proc != nullptr) {
Entity *e = entity_from_expr(proc);
if (e != nullptr && e->kind == Entity_Procedure) {
@@ -8189,16 +8430,31 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
break;
- }
-
case ProcInlining_no_inline:
break;
}
operand->expr = call;
- if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
- operand->mode = Addressing_OptionalOk;
+ {
+ if (proc_type == t_invalid) {
+ // gb_printf_err("%s\n", expr_to_string(operand->expr));
+ }
+ Type *type = nullptr;
+ if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+ type = type_of_expr(operand->expr->CallExpr.proc);
+ }
+ if (type == nullptr) {
+ type = pt;
+ }
+ type = base_type(type);
+ if (type->kind == Type_Proc && type->Proc.optional_ok) {
+ operand->mode = Addressing_OptionalOk;
+ operand->type = type->Proc.results->Tuple.variables[0]->type;
+ if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+ operand->expr->CallExpr.optional_ok_one = true;
+ }
+ }
}
// add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
@@ -9179,9 +9435,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (!is_constant) {
error(node, "Expected all constant elements for a simd vector");
}
- if (t->SimdVector.is_x86_mmx) {
- error(node, "Compound literals are not allowed with intrinsics.x86_mmx");
- }
}
@@ -9822,7 +10075,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
if (type_hint) {
+ Type *type = type_of_expr(ac->expr);
check_cast(c, o, type_hint);
+ if (is_type_typed(type) && are_types_identical(type, type_hint)) {
+ if (build_context.vet) {
+ error(node, "Redundant 'auto_cast' applied to expression");
+ } else {
+ warning(node, "Redundant 'auto_cast' applied to expression");
+ }
+ }
+
}
o->expr = node;
return Expr_Expr;
@@ -10563,7 +10825,7 @@ void check_not_tuple(CheckerContext *c, Operand *o) {
if (o->type->kind == Type_Tuple) {
isize count = o->type->Tuple.variables.count;
error(o->expr,
- "%td-valued tuple found where single value expected", count);
+ "%td-valued expression found where single value expected", count);
o->mode = Addressing_Invalid;
GB_ASSERT(count != 1);
}
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 9d02d003d..7d9eefe19 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -10,7 +10,19 @@ bool is_diverging_stmt(Ast *stmt) {
String name = expr->CallExpr.proc->BasicDirective.name;
return name == "panic";
}
- Type *t = type_of_expr(expr->CallExpr.proc);
+ Ast *proc = unparen_expr(expr->CallExpr.proc);
+ TypeAndValue tv = proc->tav;
+ if (tv.mode == Addressing_Builtin) {
+ Entity *e = entity_of_node(proc);
+ BuiltinProcId id = BuiltinProc_Invalid;
+ if (e != nullptr) {
+ id = cast(BuiltinProcId)e->Builtin.id;
+ } else {
+ id = BuiltinProc_DIRECTIVE;
+ }
+ return builtin_procs[id].diverging;
+ }
+ Type *t = tv.type;
t = base_type(t);
return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
}
@@ -1751,7 +1763,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
Type *cond_type = t->Tuple.variables[count-1]->type;
if (!is_type_boolean(cond_type)) {
gbString s = type_to_string(cond_type);
- error(operand.expr, "The final type of %td-valued tuple must be a boolean, got %s", count, s);
+ error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s);
gb_string_free(s);
break;
}
@@ -1762,14 +1774,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
gbString s = type_to_string(t);
- error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
+ error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
gb_string_free(s);
break;
}
if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
gbString s = type_to_string(t);
- error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
+ error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s);
gb_string_free(s);
break;
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 39fea75db..e3aac161c 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -2101,8 +2101,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
}
break;
}
- case Type_Pointer: break;
- case Type_Proc: break; // NOTE(bill): Just a pointer
+ case Type_Pointer:
+ if (is_type_struct(bt->Pointer.elem)) {
+ // Force to a raw pointer
+ new_type = t_rawptr;
+ }
+ break;
+ case Type_Proc:
+ new_type = t_rawptr;
+ break; // NOTE(bill): Just a pointer
// Odin specific
case Type_Slice:
@@ -2194,6 +2201,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
return new_type;
}
+ if (is_type_pointer(single_type)) {
+ // NOTE(bill): Force a cast to prevent a possible type cycle
+ return t_rawptr;
+ }
if (build_context.ODIN_OS == "windows") {
if (build_context.ODIN_ARCH == "amd64") {
@@ -2450,6 +2461,24 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
}
}
}
+ if (pt->tags & ProcTag_optional_second) {
+ if (optional_ok) {
+ error(proc_type_node, "A procedure type cannot have both an #optional_ok tag and #optional_second");
+ }
+ optional_ok = true;
+ if (result_count != 2) {
+ error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
+ } else {
+ bool ok = false;
+ if (proc_type_node->file && proc_type_node->file->pkg) {
+ ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
+ }
+
+ if (!ok) {
+ error(proc_type_node, "A procedure type with the #optional_second may only be allowed within 'package runtime'");
+ }
+ }
+ }
type->Proc.node = proc_type_node;
type->Proc.scope = c->scope;
@@ -2590,12 +2619,11 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
}
Type *make_optional_ok_type(Type *value, bool typed) {
- // LEAK TODO(bill): probably don't reallocate everything here and reuse the same one for the same type if possible
- gbAllocator a = heap_allocator();
+ gbAllocator a = permanent_allocator();
Type *t = alloc_type_tuple();
- array_init(&t->Tuple.variables, a, 0, 2);
- array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value, false, 0));
- array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1));
+ array_init(&t->Tuple.variables, a, 2);
+ t->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, value, false, 0);
+ t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1);
return t;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index e0b303369..f386d6da7 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -783,15 +783,6 @@ void init_universal(void) {
}
}
- // TODO(bill): Set the correct arch for this
- if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) {
- t_vector_x86_mmx = alloc_type(Type_SimdVector);
- t_vector_x86_mmx->SimdVector.is_x86_mmx = true;
-
- Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx);
- add_global_entity(entity, intrinsics_pkg->scope);
- }
-
bool defined_values_double_declaration = false;
for_array(i, bc->defined_values.entries) {
char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key;
@@ -1782,20 +1773,28 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("memory_equal"),
str_lit("memory_compare"),
str_lit("memory_compare_zero"),
-
- str_lit("bswap_16"),
- str_lit("bswap_32"),
- str_lit("bswap_64"),
- str_lit("bswap_128"),
-
- str_lit("bswap_f16"),
- str_lit("bswap_f32"),
- str_lit("bswap_f64"),
};
for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
}
+ if (!build_context.use_llvm_api) {
+ String other_required_runtime_entities[] = {
+ str_lit("bswap_16"),
+ str_lit("bswap_32"),
+ str_lit("bswap_64"),
+ str_lit("bswap_128"),
+
+ str_lit("bswap_f16"),
+ str_lit("bswap_f32"),
+ str_lit("bswap_f64"),
+ };
+
+ for (isize i = 0; i < gb_count_of(other_required_runtime_entities); i++) {
+ force_add_dependency_entity(c, c->info.runtime_package->scope, other_required_runtime_entities[i]);
+ }
+ }
+
if (build_context.no_crt) {
String required_no_crt_entities[] = {
// NOTE(bill): Only if these exist
@@ -2190,8 +2189,6 @@ Type *check_poly_path_pop(CheckerContext *c) {
-void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
-
Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) {
Array<Entity *> procs = {};
if (o.mode == Addressing_ProcGroup) {
@@ -2574,6 +2571,29 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
+ } else if (name == "optimization_mode") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ String mode = ev.value_string;
+ if (mode == "none") {
+ ac->optimization_mode = ProcedureOptimizationMode_None;
+ } else if (mode == "minimal") {
+ ac->optimization_mode = ProcedureOptimizationMode_Minimal;
+ } else if (mode == "size") {
+ ac->optimization_mode = ProcedureOptimizationMode_Size;
+ } else if (mode == "speed") {
+ ac->optimization_mode = ProcedureOptimizationMode_Speed;
+ } else {
+ error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
+ error_line("\tnone\n");
+ error_line("\tminimal\n");
+ error_line("\tsize\n");
+ error_line("\tspeed\n");
+ }
+ } else {
+ error(elem, "Expected a string for '%.*s'", LIT(name));
+ }
+ return true;
}
return false;
}
diff --git a/src/checker.hpp b/src/checker.hpp
index b3e0b60ec..4ff72717d 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -59,6 +59,7 @@ struct BuiltinProc {
bool variadic;
ExprKind kind;
BuiltinProcPkg pkg;
+ bool diverging;
};
@@ -112,6 +113,7 @@ struct AttributeContext {
String thread_local_model;
String deprecated_message;
DeferredProcedure deferred_procedure;
+ u32 optimization_mode; // ProcedureOptimizationMode
struct TypeAtomOpTable *atom_op_table;
};
@@ -396,6 +398,10 @@ void check_add_import_decl(CheckerContext *c, Ast *decl);
void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
+void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
+void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
+void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
+
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 7c0e0746f..b9794da8a 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -41,6 +41,18 @@ enum BuiltinProcId {
BuiltinProc_alloca,
BuiltinProc_cpu_relax,
+ BuiltinProc_trap,
+ BuiltinProc_debug_trap,
+ BuiltinProc_read_cycle_counter,
+
+ BuiltinProc_count_ones,
+ BuiltinProc_trailing_zeros,
+ BuiltinProc_reverse_bits,
+ BuiltinProc_byte_swap,
+
+ BuiltinProc_overflow_add,
+ BuiltinProc_overflow_sub,
+ BuiltinProc_overflow_mul,
BuiltinProc_volatile_store,
BuiltinProc_volatile_load,
@@ -122,6 +134,7 @@ enum BuiltinProcId {
BuiltinProc_fixed_point_mul_sat,
BuiltinProc_fixed_point_div_sat,
+ BuiltinProc_expect,
// Constant type tests
@@ -246,6 +259,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/true},
+ {STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
+ {STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("overflow_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -327,6 +353,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("fixed_point_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index c3aaebf64..e620da51f 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -114,10 +114,6 @@ enum OdinDocTypeFlag_BitSet : u32 {
OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4,
};
-enum OdinDocTypeFlag_SimdVector : u32 {
- OdinDocTypeFlag_BitSet_x86_mmx = 1<<1,
-};
-
enum {
// constants
OdinDocType_ElemsCap = 4,
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 5c9bb9f63..f4fd02376 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -724,13 +724,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
break;
case Type_SimdVector:
doc_type.kind = OdinDocType_SimdVector;
- if (type->SimdVector.is_x86_mmx) {
- doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx;
- } else {
- doc_type.elem_count_len = 1;
- doc_type.elem_counts[0] = type->SimdVector.count;
- doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
- }
+ doc_type.elem_count_len = 1;
+ doc_type.elem_counts[0] = type->SimdVector.count;
+ doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
// TODO(bill):
break;
case Type_RelativePointer:
diff --git a/src/entity.cpp b/src/entity.cpp
index 3926678fd..d1f4c78e6 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -33,39 +33,41 @@ String const entity_strings[] = {
};
enum EntityFlag : u64 {
- EntityFlag_Visited = 1<<0,
- EntityFlag_Used = 1<<1,
- EntityFlag_Using = 1<<2,
- EntityFlag_Field = 1<<3,
- EntityFlag_Param = 1<<4,
- EntityFlag_Result = 1<<5,
- EntityFlag_ArrayElem = 1<<6,
- EntityFlag_Ellipsis = 1<<7,
- EntityFlag_NoAlias = 1<<8,
- EntityFlag_TypeField = 1<<9,
- EntityFlag_Value = 1<<10,
- EntityFlag_Sret = 1<<11,
- EntityFlag_ByVal = 1<<12,
- EntityFlag_BitFieldValue = 1<<13,
- EntityFlag_PolyConst = 1<<14,
- EntityFlag_NotExported = 1<<15,
- EntityFlag_ConstInput = 1<<16,
+ EntityFlag_Visited = 1ull<<0,
+ EntityFlag_Used = 1ull<<1,
+ EntityFlag_Using = 1ull<<2,
+ EntityFlag_Field = 1ull<<3,
+ EntityFlag_Param = 1ull<<4,
+ EntityFlag_Result = 1ull<<5,
+ EntityFlag_ArrayElem = 1ull<<6,
+ EntityFlag_Ellipsis = 1ull<<7,
+ EntityFlag_NoAlias = 1ull<<8,
+ EntityFlag_TypeField = 1ull<<9,
+ EntityFlag_Value = 1ull<<10,
+ EntityFlag_Sret = 1ull<<11,
+ EntityFlag_ByVal = 1ull<<12,
+ EntityFlag_BitFieldValue = 1ull<<13,
+ EntityFlag_PolyConst = 1ull<<14,
+ EntityFlag_NotExported = 1ull<<15,
+ EntityFlag_ConstInput = 1ull<<16,
- EntityFlag_Static = 1<<17,
+ EntityFlag_Static = 1ull<<17,
- EntityFlag_ImplicitReference = 1<<18, // NOTE(bill): equivalent to `const &` in C++
+ EntityFlag_ImplicitReference = 1ull<<18, // NOTE(bill): equivalent to `const &` in C++
- EntityFlag_SoaPtrField = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
+ EntityFlag_SoaPtrField = 1ull<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
- EntityFlag_ProcBodyChecked = 1<<20,
+ EntityFlag_ProcBodyChecked = 1ull<<20,
- EntityFlag_CVarArg = 1<<21,
- EntityFlag_AutoCast = 1<<22,
+ EntityFlag_CVarArg = 1ull<<21,
+ EntityFlag_AutoCast = 1ull<<22,
- EntityFlag_Disabled = 1<<24,
- EntityFlag_Cold = 1<<25, // procedure is rarely called
+ EntityFlag_Disabled = 1ull<<24,
+ EntityFlag_Cold = 1ull<<25, // procedure is rarely called
- EntityFlag_Test = 1<<30,
+ EntityFlag_Test = 1ull<<30,
+
+ EntityFlag_Overridden = 1ull<<63,
};
@@ -97,6 +99,14 @@ enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
+enum ProcedureOptimizationMode : u32 {
+ ProcedureOptimizationMode_Default,
+ ProcedureOptimizationMode_None,
+ ProcedureOptimizationMode_Minimal,
+ ProcedureOptimizationMode_Size,
+ ProcedureOptimizationMode_Speed,
+};
+
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -126,6 +136,9 @@ struct Entity {
// later entity kinds
union {
struct {
+ u8 start;
+ } Dummy;
+ struct {
ExactValue value;
ParameterValue param_value;
u32 flags;
@@ -160,6 +173,7 @@ struct Entity {
DeferredProcedure deferred_procedure;
bool is_foreign;
bool is_export;
+ ProcedureOptimizationMode optimization_mode;
} Procedure;
struct {
Array<Entity *> entities;
diff --git a/src/gb/gb.h b/src/gb/gb.h
index b56c20f1e..60e7b8e64 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -491,8 +491,8 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
#define USIZE_MIX U32_MIN
#define USIZE_MAX U32_MAX
- #define ISIZE_MIX S32_MIN
- #define ISIZE_MAX S32_MAX
+ #define ISIZE_MIX I32_MIN
+ #define ISIZE_MAX I32_MAX
#elif defined(GB_ARCH_64_BIT)
#define USIZE_MIX U64_MIN
#define USIZE_MAX U64_MAX
diff --git a/src/ir.cpp b/src/ir.cpp
index 0ad48ca27..1c81f08ed 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8257,7 +8257,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
case_end;
case_ast_node(ac, AutoCast, expr);
- return ir_build_expr(proc, ac->expr);
+ irValue *value = ir_build_expr(proc, ac->expr);
+ return ir_emit_conv(proc, value, tv.type);
case_end;
case_ast_node(ue, UnaryExpr, expr);
@@ -12564,13 +12565,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
case Type_SimdVector:
ir_emit_comment(proc, str_lit("Type_SimdVector"));
tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr);
- if (t->SimdVector.is_x86_mmx) {
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true);
- } else {
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
- ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
- }
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
+ ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
break;
case Type_RelativePointer:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index d0c014a27..54ce5dca1 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -624,13 +624,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
}
case Type_SimdVector:
- if (t->SimdVector.is_x86_mmx) {
- ir_write_str_lit(f, "x86_mmx");
- } else {
- ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
- ir_print_type(f, m, t->SimdVector.elem);
- ir_write_byte(f, '>');
- }
+ ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
+ ir_print_type(f, m, t->SimdVector.elem);
+ ir_write_byte(f, '>');
return;
case Type_RelativePointer:
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 62d5a58a6..d3bc3d0f8 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -391,11 +391,37 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return;
} else if (addr.kind == lbAddr_Context) {
- lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p));
- lbAddr next_addr = lb_add_local_generated(p, t_context, true);
- lb_addr_store(p, next_addr, old);
- lb_push_context_onto_stack(p, next_addr);
- lbValue next = lb_addr_get_ptr(p, next_addr);
+ lbAddr old_addr = lb_find_or_generate_context_ptr(p);
+
+
+ // IMPORTANT NOTE(bill, 2021-04-22): reuse unused 'context' variables to minimize stack usage
+ // This has to be done manually since the optimizer cannot determine when this is possible
+ bool create_new = true;
+ for_array(i, p->context_stack) {
+ lbContextData *ctx_data = &p->context_stack[i];
+ if (ctx_data->ctx.addr.value == old_addr.addr.value) {
+ if (ctx_data->uses > 0) {
+ create_new = true;
+ } else if (p->scope_index > ctx_data->scope_index) {
+ create_new = true;
+ } else {
+ // gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses);
+ create_new = false;
+ }
+ break;
+ }
+ }
+
+ lbValue next = {};
+ if (create_new) {
+ lbValue old = lb_addr_load(p, old_addr);
+ lbAddr next_addr = lb_add_local_generated(p, t_context, true);
+ lb_addr_store(p, next_addr, old);
+ lb_push_context_onto_stack(p, next_addr);
+ next = next_addr.addr;
+ } else {
+ next = old_addr.addr;
+ }
if (addr.ctx.sel.index.count > 0) {
lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel);
@@ -623,6 +649,13 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
}
} else if (addr.kind == lbAddr_Context) {
lbValue a = addr.addr;
+ for_array(i, p->context_stack) {
+ lbContextData *ctx_data = &p->context_stack[i];
+ if (ctx_data->ctx.addr.value == a.value) {
+ ctx_data->uses += 1;
+ break;
+ }
+ }
a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), "");
if (addr.ctx.sel.index.count > 0) {
@@ -1445,9 +1478,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
case Type_SimdVector:
- if (type->SimdVector.is_x86_mmx) {
- return LLVMX86MMXTypeInContext(ctx);
- }
return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
case Type_RelativePointer:
@@ -1899,9 +1929,6 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
break;
case Type_SimdVector:
- if (type->SimdVector.is_x86_mmx) {
- return LLVMDIBuilderCreateVectorType(m->debug_builder, 2, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, t_f64), nullptr, 0);
- }
return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
case Type_RelativePointer: {
@@ -2523,7 +2550,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
p->type = entity->type;
p->type_expr = decl->type_expr;
p->body = pl->body;
- p->inlining = ProcInlining_none;
+ p->inlining = pl->inlining;
p->is_foreign = entity->Procedure.is_foreign;
p->is_export = entity->Procedure.is_export;
p->is_entry_point = false;
@@ -2558,9 +2585,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
LLVMSetFunctionCallConv(p->value, cc_kind);
}
- if (entity->flags & EntityFlag_Cold) {
- lb_add_attribute_to_proc(m, p->value, "cold");
- }
if (pt->Proc.diverging) {
lb_add_attribute_to_proc(m, p->value, "noreturn");
@@ -2575,6 +2599,28 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
break;
}
+ if (entity->flags & EntityFlag_Cold) {
+ lb_add_attribute_to_proc(m, p->value, "cold");
+ }
+
+ switch (entity->Procedure.optimization_mode) {
+ case ProcedureOptimizationMode_None:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Minimal:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Size:
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
+ case ProcedureOptimizationMode_Speed:
+ // TODO(bill): handle this correctly
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
+ }
+
+
+
// lbCallingConventionKind cc_kind = lbCallingConvention_C;
// // TODO(bill): Clean up this logic
// if (build_context.metrics.os != TargetOs_js) {
@@ -2624,26 +2670,16 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
for (isize i = 0; i < pt->Proc.param_count; i++) {
Entity *e = params->variables[i];
Type *original_type = e->type;
- Type *abi_type = pt->Proc.abi_compat_params[i];
if (e->kind != Entity_Variable) continue;
if (i+1 == params->variables.count && pt->Proc.c_vararg) {
continue;
}
- if (is_type_tuple(abi_type)) {
- for_array(j, abi_type->Tuple.variables) {
- Type *tft = abi_type->Tuple.variables[j]->type;
- if (e->flags&EntityFlag_NoAlias) {
- lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias");
- }
- }
- parameter_index += abi_type->Tuple.variables.count;
- } else {
- if (e->flags&EntityFlag_NoAlias) {
- lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
- }
- parameter_index += 1;
+
+ if (e->flags&EntityFlag_NoAlias) {
+ lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
}
+ parameter_index += 1;
}
}
@@ -7567,6 +7603,7 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p
lbContextData *cd = array_add_and_get(&p->context_stack);
cd->ctx = ctx_addr;
cd->scope_index = -1;
+ cd->uses = +1; // make sure it has been used already
return cd;
}
@@ -9031,6 +9068,145 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
return {};
+
+ case BuiltinProc_debug_trap:
+ case BuiltinProc_trap:
+ {
+ char const *name = nullptr;
+ switch (id) {
+ case BuiltinProc_debug_trap: name = "llvm.debugtrap"; break;
+ case BuiltinProc_trap: name = "llvm.trap"; break;
+ }
+
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+ LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ if (id == BuiltinProc_trap) {
+ LLVMBuildUnreachable(p->builder);
+ }
+ return {};
+ }
+
+ case BuiltinProc_read_cycle_counter:
+ {
+ char const *name = "llvm.readcyclecounter";
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ res.type = tv.type;
+ return res;
+ }
+
+ case BuiltinProc_trailing_zeros:
+ {
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ x = lb_emit_conv(p, x, tv.type);
+
+ char const *name = "llvm.cttz";
+ LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = x.value;
+ args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = tv.type;
+ return res;
+ }
+
+ case BuiltinProc_count_ones:
+ case BuiltinProc_reverse_bits:
+ {
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ x = lb_emit_conv(p, x, tv.type);
+
+ char const *name = nullptr;
+ switch (id) {
+ case BuiltinProc_count_ones: name = "llvm.ctpop"; break;
+ case BuiltinProc_reverse_bits: name = "llvm.bitreverse"; break;
+ }
+ LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = x.value;
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = tv.type;
+ return res;
+ }
+
+ case BuiltinProc_byte_swap:
+ {
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ x = lb_emit_conv(p, x, tv.type);
+ return lb_emit_byte_swap(p, x, tv.type);
+ }
+
+ case BuiltinProc_overflow_add:
+ case BuiltinProc_overflow_sub:
+ case BuiltinProc_overflow_mul:
+ {
+ Type *tuple = tv.type;
+ GB_ASSERT(is_type_tuple(tuple));
+ Type *type = tuple->Tuple.variables[0]->type;
+
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ lbValue y = lb_build_expr(p, ce->args[1]);
+ x = lb_emit_conv(p, x, type);
+ y = lb_emit_conv(p, y, type);
+
+ char const *name = nullptr;
+ if (is_type_unsigned(type)) {
+ switch (id) {
+ case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break;
+ case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break;
+ case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break;
+ }
+ } else {
+ switch (id) {
+ case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break;
+ case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break;
+ case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break;
+ }
+ }
+ LLVMTypeRef types[1] = {lb_type(p->module, type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = x.value;
+ args[1] = y.value;
+
+ Type *res_type = nullptr;
+ {
+ gbAllocator a = permanent_allocator();
+ res_type = alloc_type_tuple();
+ array_init(&res_type->Tuple.variables, a, 2);
+ res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0);
+ res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1);
+ }
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = res_type;
+ return res;
+ }
+
+
case BuiltinProc_atomic_fence:
LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
return {};
@@ -9304,6 +9480,30 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = platform_type;
return lb_emit_conv(p, res, tv.type);
}
+
+ case BuiltinProc_expect:
+ {
+ Type *t = default_type(tv.type);
+ lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t);
+ lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t);
+
+ char const *name = "llvm.expect";
+
+ LLVMTypeRef types[1] = {lb_type(p->module, t)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ lbValue res = {};
+
+ LLVMValueRef args[2] = {};
+ args[0] = x.value;
+ args[1] = y.value;
+
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = t;
+ return lb_emit_conv(p, res, t);
+ }
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
@@ -9692,6 +9892,7 @@ bool lb_is_const_nil(lbValue value) {
String lb_get_const_string(lbModule *m, lbValue value) {
GB_ASSERT(lb_is_const(value));
+ GB_ASSERT(LLVMIsConstant(value.value));
Type *t = base_type(value.type);
GB_ASSERT(are_types_identical(t, t_string));
@@ -9743,43 +9944,43 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) {
return found->value;
}
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
- Type *vt = core_type(value.type);
- GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
-
- // TODO(bill): lb_emit_byte_swap
- lbValue res = {};
- res.type = platform_type;
- res.value = value.value;
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
+ GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
- int sz = cast(int)type_size_of(vt);
- if (sz > 1) {
- if (is_type_float(platform_type)) {
- String name = {};
- switch (sz) {
- case 2: name = str_lit("bswap_f16"); break;
- case 4: name = str_lit("bswap_f32"); break;
- case 8: name = str_lit("bswap_f64"); break;
- default: GB_PANIC("unhandled byteswap size"); break;
- }
- LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
- res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
- } else {
- GB_ASSERT(is_type_integer(platform_type));
- String name = {};
- switch (sz) {
- case 2: name = str_lit("bswap_16"); break;
- case 4: name = str_lit("bswap_32"); break;
- case 8: name = str_lit("bswap_64"); break;
- case 16: name = str_lit("bswap_128"); break;
- default: GB_PANIC("unhandled byteswap size"); break;
- }
- LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
+ if (type_size_of(value.type) < 2) {
+ return value;
+ }
- res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+ Type *original_type = value.type;
+ if (is_type_float(original_type)) {
+ i64 sz = type_size_of(original_type);
+ Type *integer_type = nullptr;
+ switch (sz) {
+ case 2: integer_type = t_u16; break;
+ case 4: integer_type = t_u32; break;
+ case 8: integer_type = t_u64; break;
}
+ GB_ASSERT(integer_type != nullptr);
+ value = lb_emit_transmute(p, value, integer_type);
}
+ char const *name = "llvm.bswap";
+ LLVMTypeRef types[1] = {lb_type(p->module, value.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = value.value;
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = value.type;
+
+ if (is_type_float(original_type)) {
+ res = lb_emit_transmute(p, res, original_type);
+ }
+ res.type = end_type;
return res;
}
@@ -10963,7 +11164,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(ac, AutoCast, expr);
- return lb_build_expr(p, ac->expr);
+ lbValue value = lb_build_expr(p, ac->expr);
+ return lb_emit_conv(p, value, tv.type);
case_end;
case_ast_node(ue, UnaryExpr, expr);
@@ -13355,15 +13557,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
{
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
- LLVMValueRef vals[4] = {};
+ LLVMValueRef vals[3] = {};
- if (t->SimdVector.is_x86_mmx) {
- vals[3] = lb_const_bool(m, t_bool, true).value;
- } else {
- vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
- vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
- vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
- }
+ vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
+ vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
+ vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
lbValue res = {};
res.type = type_deref(tag.type);
@@ -13844,11 +14042,28 @@ void lb_generate_code(lbGenerator *gen) {
LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
+ LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod);
+ LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod);
+ LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod);
defer (LLVMDisposePassManager(default_function_pass_manager));
+ defer (LLVMDisposePassManager(function_pass_manager_minimal));
+ defer (LLVMDisposePassManager(function_pass_manager_size));
+ defer (LLVMDisposePassManager(function_pass_manager_speed));
LLVMInitializeFunctionPassManager(default_function_pass_manager);
+ LLVMInitializeFunctionPassManager(function_pass_manager_minimal);
+ LLVMInitializeFunctionPassManager(function_pass_manager_size);
+ LLVMInitializeFunctionPassManager(function_pass_manager_speed);
+
lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level);
+ lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0);
+ lb_populate_function_pass_manager_specific(function_pass_manager_size, 1);
+ lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2);
+
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+ LLVMFinalizeFunctionPassManager(function_pass_manager_minimal);
+ LLVMFinalizeFunctionPassManager(function_pass_manager_size);
+ LLVMFinalizeFunctionPassManager(function_pass_manager_speed);
LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod);
@@ -13988,6 +14203,47 @@ void lb_generate_code(lbGenerator *gen) {
}*/
}
+ String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
+
+ TIME_SECTION("LLVM Procedure Generation");
+ for_array(i, m->procedures_to_generate) {
+ lbProcedure *p = m->procedures_to_generate[i];
+ if (p->is_done) {
+ continue;
+ }
+ 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;
+ m->curr_procedure = nullptr;
+ }
+ lb_end_procedure(p);
+
+ // Add Flags
+ if (p->body != nullptr) {
+ if (p->name == "memcpy" || p->name == "memmove" ||
+ p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
+ string_starts_with(p->name, str_lit("llvm.memcpy")) ||
+ string_starts_with(p->name, str_lit("llvm.memmove"))) {
+ p->flags |= lbProcedureFlag_WithoutMemcpyPass;
+ }
+ }
+
+ if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+ gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
+ LLVMDumpValue(p->value);
+ gb_printf_err("\n\n\n\n");
+ if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+ gb_printf_err("LLVM Error: %s\n", llvm_error);
+ }
+ LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
+ gb_exit(1);
+ }
+ }
+
+
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
TIME_SECTION("LLVM DLL main");
@@ -14075,7 +14331,7 @@ void lb_generate_code(lbGenerator *gen) {
} else {
lbValue *found = map_get(&m->values, hash_entity(entry_point));
GB_ASSERT(found != nullptr);
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
+ lb_emit_call(p, *found, {});
}
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
@@ -14093,47 +14349,6 @@ void lb_generate_code(lbGenerator *gen) {
}
- String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
-
- TIME_SECTION("LLVM Procedure Generation");
- for_array(i, m->procedures_to_generate) {
- lbProcedure *p = m->procedures_to_generate[i];
- if (p->is_done) {
- continue;
- }
- 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;
- m->curr_procedure = nullptr;
- }
- lb_end_procedure(p);
-
- // Add Flags
- if (p->body != nullptr) {
- if (p->name == "memcpy" || p->name == "memmove" ||
- p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
- string_starts_with(p->name, str_lit("llvm.memcpy")) ||
- string_starts_with(p->name, str_lit("llvm.memmove"))) {
- p->flags |= lbProcedureFlag_WithoutMemcpyPass;
- }
- }
-
- if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
- gb_printf_err("LLVM Error: %s\n", llvm_error);
- }
- LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
- gb_exit(1);
- }
- }
-
-
if (m->debug_builder != nullptr) {
TIME_SECTION("LLVM Debug Info Complete Types");
lb_debug_complete_types(m);
@@ -14158,7 +14373,25 @@ void lb_generate_code(lbGenerator *gen) {
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
} else {
- LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+ if (p->entity && p->entity->kind == Entity_Procedure) {
+ switch (p->entity->Procedure.optimization_mode) {
+ case ProcedureOptimizationMode_None:
+ case ProcedureOptimizationMode_Minimal:
+ LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value);
+ break;
+ case ProcedureOptimizationMode_Size:
+ LLVMRunFunctionPassManager(function_pass_manager_size, p->value);
+ break;
+ case ProcedureOptimizationMode_Speed:
+ LLVMRunFunctionPassManager(function_pass_manager_speed, p->value);
+ break;
+ default:
+ LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+ break;
+ }
+ } else {
+ LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+ }
}
}
}
@@ -14220,13 +14453,18 @@ void lb_generate_code(lbGenerator *gen) {
return;
}
llvm_error = nullptr;
- if (build_context.keep_temp_files) {
+ if (build_context.keep_temp_files ||
+ build_context.build_mode == BuildMode_LLVM_IR) {
TIME_SECTION("LLVM Print Module to File");
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
gb_exit(1);
return;
}
+ if (build_context.build_mode == BuildMode_LLVM_IR) {
+ gb_exit(0);
+ return;
+ }
}
TIME_SECTION("LLVM Object Generation");
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 8117271c1..004d6b905 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -157,6 +157,7 @@ struct lbBranchBlocks {
struct lbContextData {
lbAddr ctx;
isize scope_index;
+ isize uses;
};
enum lbParamPasskind {
@@ -310,7 +311,7 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type);
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 5e1154af2..3b268dffa 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -1,11 +1,41 @@
+void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
+void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
+void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
+void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level);
+
+void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVMAddConstantPropagationPass(fpm);
+ LLVMAddEarlyCSEPass(fpm);
+
+ LLVMAddConstantPropagationPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddCFGSimplificationPass(fpm);
+}
+
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
- if (!ignore_memcpy_pass) {
+ if (ignore_memcpy_pass) {
+ lb_basic_populate_function_pass_manager(fpm);
+ return;
+ } else if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
+ lb_basic_populate_function_pass_manager(fpm);
+ return;
}
+
+#if 0
+ LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
+ LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
+ LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
+ LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
+#else
+ LLVMAddMemCpyOptPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVMAddConstantPropagationPass(fpm);
@@ -16,16 +46,45 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddCFGSimplificationPass(fpm);
- // LLVMAddSLPVectorizePass(fpm);
- // LLVMAddLoopVectorizePass(fpm);
+ LLVMAddSCCPPass(fpm);
+
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddUnifyFunctionExitNodesPass(fpm);
+
+ LLVMAddCFGSimplificationPass(fpm);
+ LLVMAddEarlyCSEPass(fpm);
+ LLVMAddLowerExpectIntrinsicPass(fpm);
+#endif
+}
+
+void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) {
+ // NOTE(bill): Treat -opt:3 as if it was -opt:2
+ // TODO(bill): Determine which opt definitions should exist in the first place
+ optimization_level = gb_clamp(optimization_level, 0, 2);
- // LLVMAddScalarizerPass(fpm);
- // LLVMAddLoopIdiomPass(fpm);
if (optimization_level == 0) {
+ LLVMAddMemCpyOptPass(fpm);
+ lb_basic_populate_function_pass_manager(fpm);
return;
}
#if 1
+ LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
+ LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
+ LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
+ LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
+#else
+ LLVMAddMemCpyOptPass(fpm);
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVMAddConstantPropagationPass(fpm);
+ LLVMAddEarlyCSEPass(fpm);
+
+ LLVMAddConstantPropagationPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddCFGSimplificationPass(fpm);
+
LLVMAddSCCPPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
diff --git a/src/main.cpp b/src/main.cpp
index 20190a187..aa498e84d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -571,6 +571,7 @@ enum BuildFlagKind {
BuildFlag_OutFile,
BuildFlag_OptimizationLevel,
+ BuildFlag_OptimizationMode,
BuildFlag_ShowTimings,
BuildFlag_ShowUnused,
BuildFlag_ShowUnusedWithLocation,
@@ -687,6 +688,8 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
@@ -885,8 +888,36 @@ bool parse_build_flags(Array<String> args) {
}
case BuildFlag_OptimizationLevel:
GB_ASSERT(value.kind == ExactValue_Integer);
+ if (set_flags[BuildFlag_OptimizationMode]) {
+ gb_printf_err("Mixture of -opt and -o is not allowed\n");
+ bad_flags = true;
+ break;
+ }
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
break;
+ case BuildFlag_OptimizationMode:
+ GB_ASSERT(value.kind == ExactValue_String);
+ if (set_flags[BuildFlag_OptimizationLevel]) {
+ gb_printf_err("Mixture of -opt and -o is not allowed\n");
+ bad_flags = true;
+ break;
+ }
+
+ if (value.value_string == "minimal") {
+ build_context.optimization_level = 0;
+ } else if (value.value_string == "size") {
+ build_context.optimization_level = 1;
+ } else if (value.value_string == "speed") {
+ build_context.optimization_level = 2;
+ } else {
+ gb_printf_err("Invalid optimization mode for -o:<string>, got %.*s\n", LIT(value.value_string));
+ gb_printf_err("Valid optimization modes:\n");
+ gb_printf_err("\tminimal\n");
+ gb_printf_err("\tsize\n");
+ gb_printf_err("\tspeed\n");
+ bad_flags = true;
+ }
+ break;
case BuildFlag_ShowTimings:
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.show_timings = true;
@@ -1109,8 +1140,16 @@ bool parse_build_flags(Array<String> args) {
build_context.build_mode = BuildMode_Executable;
} else if (str == "asm" || str == "assembly" || str == "assembler") {
build_context.build_mode = BuildMode_Assembly;
+ } else if (str == "llvm" || str == "llvm-ir") {
+ build_context.build_mode = BuildMode_LLVM_IR;
} else {
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
+ gb_printf_err("Valid build modes:\n");
+ gb_printf_err("\tdll, shared\n");
+ gb_printf_err("\tobj, object\n");
+ gb_printf_err("\texe\n");
+ gb_printf_err("\tasm, assembly, assembler\n");
+ gb_printf_err("\tllvm, llvm-ir\n");
bad_flags = true;
break;
}
@@ -1612,6 +1651,10 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-all-packages");
print_usage_line(2, "Generates documentation for all packages used in the current project");
print_usage_line(0, "");
+
+ print_usage_line(1, "-doc-format");
+ print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)");
+ print_usage_line(0, "");
}
if (run_or_build) {
@@ -1621,10 +1664,16 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "");
print_usage_line(1, "-opt:<integer>");
- print_usage_line(2, "Set the optimization level for complication");
+ print_usage_line(2, "Set the optimization level for compilation");
print_usage_line(2, "Accepted values: 0, 1, 2, 3");
print_usage_line(2, "Example: -opt:2");
print_usage_line(0, "");
+
+ print_usage_line(1, "-o:<string>");
+ print_usage_line(2, "Set the optimization mode for compilation");
+ print_usage_line(2, "Accepted values: minimal, size, speed");
+ print_usage_line(2, "Example: -o:speed");
+ print_usage_line(0, "");
}
if (check) {
diff --git a/src/parser.cpp b/src/parser.cpp
index 86419cd8f..18a4ba9d2 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1813,6 +1813,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
if (false) {}
ELSE_IF_ADD_TAG(optional_ok)
+ ELSE_IF_ADD_TAG(optional_second)
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check)
diff --git a/src/parser.hpp b/src/parser.hpp
index 47e860845..1ff073f91 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -203,6 +203,7 @@ enum ProcTag {
ProcTag_require_results = 1<<4,
ProcTag_optional_ok = 1<<5,
+ ProcTag_optional_second = 1<<6,
};
enum ProcCallingConvention {
@@ -338,6 +339,7 @@ AST_KIND(_ExprBegin, "", bool) \
Token ellipsis; \
ProcInlining inlining; \
bool optional_ok_one; \
+ i32 builtin_id; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \
diff --git a/src/types.cpp b/src/types.cpp
index 7d85aa6bb..a5c5c2eb2 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -283,7 +283,6 @@ struct TypeProc {
TYPE_KIND(SimdVector, struct { \
i64 count; \
Type *elem; \
- bool is_x86_mmx; \
}) \
TYPE_KIND(RelativePointer, struct { \
Type *pointer_type; \
@@ -460,8 +459,8 @@ gb_global Type basic_types[] = {
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
- {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
- {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
+ {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
+ {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
{Type_Basic, {Basic_rune, BasicFlag_Integer | BasicFlag_Rune, 4, STR_LIT("rune")}},
@@ -679,8 +678,6 @@ gb_global Type *t_source_code_location_ptr = nullptr;
gb_global Type *t_map_hash = nullptr;
gb_global Type *t_map_header = nullptr;
-gb_global Type *t_vector_x86_mmx = nullptr;
-
gb_global Type *t_equal_proc = nullptr;
gb_global Type *t_hasher_proc = nullptr;
@@ -1012,6 +1009,20 @@ bool is_type_integer(Type *t) {
}
return false;
}
+bool is_type_integer_like(Type *t) {
+ t = core_type(t);
+ if (t->kind == Type_Basic) {
+ return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
+ }
+ if (t->kind == Type_BitSet) {
+ if (t->BitSet.underlying) {
+ return is_type_integer_like(t->BitSet.underlying);
+ }
+ return true;
+ }
+ return false;
+}
+
bool is_type_unsigned(Type *t) {
t = base_type(t);
// t = core_type(t);
@@ -1468,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) {
if (t->kind == Type_BitSet) {
t = bit_set_to_int(t);
}
- GB_ASSERT(t->kind == Type_Basic);
+ GB_ASSERT_MSG(t->kind == Type_Basic, "%s", type_to_string(t));
switch (t->Basic.kind) {
// Endian Specific Types
@@ -2148,12 +2159,8 @@ bool are_types_identical(Type *x, Type *y) {
case Type_SimdVector:
if (y->kind == Type_SimdVector) {
- if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) {
- if (x->SimdVector.is_x86_mmx) {
- return true;
- } else if (x->SimdVector.count == y->SimdVector.count) {
- return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
- }
+ if (x->SimdVector.count == y->SimdVector.count) {
+ return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
}
}
break;
@@ -2953,9 +2960,6 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
}
case Type_SimdVector: {
- if (t->SimdVector.is_x86_mmx) {
- return 8;
- }
// align of
i64 count = t->SimdVector.count;
Type *elem = t->SimdVector.elem;
@@ -3219,9 +3223,6 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
}
case Type_SimdVector: {
- if (t->SimdVector.is_x86_mmx) {
- return 8;
- }
i64 count = t->SimdVector.count;
Type *elem = t->SimdVector.elem;
return count * type_size_of_internal(elem, path);
@@ -3656,12 +3657,8 @@ gbString write_type_to_string(gbString str, Type *type) {
break;
case Type_SimdVector:
- if (type->SimdVector.is_x86_mmx) {
- return gb_string_appendc(str, "intrinsics.x86_mmx");
- } else {
- str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
- str = write_type_to_string(str, type->SimdVector.elem);
- }
+ str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
+ str = write_type_to_string(str, type->SimdVector.elem);
break;
case Type_RelativePointer: