mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-22 23:47:51 +00:00
Merge remote-tracking branch 'offical/master'
# Conflicts: # src/check_builtin.cpp
This commit is contained in:
@@ -844,6 +844,8 @@ struct BuildContext {
|
||||
bool show_unused;
|
||||
bool show_unused_with_location;
|
||||
bool show_more_timings;
|
||||
bool show_defineables;
|
||||
String export_defineables_file;
|
||||
bool show_system_calls;
|
||||
bool keep_temp_files;
|
||||
bool ignore_unknown_attributes;
|
||||
|
||||
+81
-9
@@ -1756,9 +1756,21 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_bool(is_defined);
|
||||
|
||||
// If the arg is a selector expression we don't add it, `-define` only allows identifiers.
|
||||
if (arg->kind == Ast_Ident) {
|
||||
Defineable defineable = {};
|
||||
defineable.docs = nullptr;
|
||||
defineable.name = arg->Ident.token.string;
|
||||
defineable.default_value = exact_value_bool(false);
|
||||
defineable.pos = arg->Ident.token.pos;
|
||||
|
||||
MUTEX_GUARD(&c->info->defineables_mutex);
|
||||
array_add(&c->info->defineables, defineable);
|
||||
}
|
||||
|
||||
} else if (name == "config") {
|
||||
if (ce->args.count != 2) {
|
||||
error(call, "'#config' expects 2 argument, got %td", ce->args.count);
|
||||
error(call, "'#config' expects 2 arguments, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
Ast *arg = unparen_expr(ce->args[0]);
|
||||
@@ -1793,8 +1805,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
operand->value = found->Constant.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Defineable defineable = {};
|
||||
defineable.docs = nullptr;
|
||||
defineable.name = name;
|
||||
defineable.default_value = def.value;
|
||||
defineable.pos = arg->Ident.token.pos;
|
||||
|
||||
if (c->decl) {
|
||||
defineable.docs = c->decl->docs;
|
||||
}
|
||||
|
||||
MUTEX_GUARD(&c->info->defineables_mutex);
|
||||
array_add(&c->info->defineables, defineable);
|
||||
|
||||
}
|
||||
|
||||
// Bodging in #region & #endregion support
|
||||
else if (name == "region") {
|
||||
operand->type = t_untyped_bool;
|
||||
@@ -1806,7 +1832,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_bool(true);
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
error(call, "Unknown directive call: #%.*s", LIT(name));
|
||||
}
|
||||
@@ -5103,15 +5129,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
isize max_arg_count = 32;
|
||||
|
||||
switch (build_context.metrics.os) {
|
||||
case TargetOs_windows:
|
||||
case TargetOs_freestanding:
|
||||
error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
|
||||
break;
|
||||
case TargetOs_darwin:
|
||||
case TargetOs_linux:
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
case TargetOs_haiku:
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_i386:
|
||||
@@ -5121,6 +5141,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
|
||||
break;
|
||||
}
|
||||
|
||||
if (ce->args.count > max_arg_count) {
|
||||
@@ -5134,6 +5157,55 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case BuiltinProc_syscall_bsd:
|
||||
{
|
||||
convert_to_typed(c, operand, t_uintptr);
|
||||
if (!is_type_uintptr(operand->type)) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Argument 0 must be of type 'uintptr', got %s", t);
|
||||
gb_string_free(t);
|
||||
}
|
||||
for (isize i = 1; i < ce->args.count; i++) {
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[i]);
|
||||
if (x.mode != Addressing_Invalid) {
|
||||
convert_to_typed(c, &x, t_uintptr);
|
||||
}
|
||||
convert_to_typed(c, &x, t_uintptr);
|
||||
if (!is_type_uintptr(x.type)) {
|
||||
gbString t = type_to_string(x.type);
|
||||
error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
|
||||
gb_string_free(t);
|
||||
}
|
||||
}
|
||||
|
||||
isize max_arg_count = 32;
|
||||
|
||||
switch (build_context.metrics.os) {
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_netbsd:
|
||||
case TargetOs_openbsd:
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_amd64:
|
||||
case TargetArch_arm64:
|
||||
max_arg_count = 7;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
|
||||
break;
|
||||
}
|
||||
|
||||
if (ce->args.count > max_arg_count) {
|
||||
error(ast_end_token(call), "'%.*s' has a maximum of %td arguments on this platform (%.*s), got %td", LIT(builtin_name), max_arg_count, LIT(target_os_names[build_context.metrics.os]), ce->args.count);
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = make_optional_ok_type(t_uintptr);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case BuiltinProc_type_base_type:
|
||||
|
||||
+1
-1
@@ -1077,7 +1077,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
|
||||
|
||||
if (e->pkg != nullptr && e->token.string == "main") {
|
||||
if (e->pkg != nullptr && e->token.string == "main" && !build_context.no_entry_point) {
|
||||
if (e->pkg->kind != Package_Runtime) {
|
||||
if (pt->param_count != 0 ||
|
||||
pt->result_count != 0) {
|
||||
|
||||
+16
-1
@@ -2606,6 +2606,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
|
||||
if (o->mode == Addressing_Constant) {
|
||||
Type *type = base_type(o->type);
|
||||
if (!is_type_constant_type(o->type)) {
|
||||
if (is_type_array_like(o->type)) {
|
||||
o->mode = Addressing_Value;
|
||||
return;
|
||||
}
|
||||
|
||||
gbString xt = type_to_string(o->type);
|
||||
gbString err_str = expr_to_string(node);
|
||||
error(op, "Invalid type, '%s', for constant unary expression '%s'", xt, err_str);
|
||||
@@ -8324,6 +8329,14 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
|
||||
}
|
||||
o->type = t_untyped_string;
|
||||
o->value = exact_value_string(file);
|
||||
} else if (name == "directory") {
|
||||
String file = get_file_path_string(bd->token.pos.file_id);
|
||||
String path = dir_from_path(file);
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
path = obfuscate_string(path, "D");
|
||||
}
|
||||
o->type = t_untyped_string;
|
||||
o->value = exact_value_string(path);
|
||||
} else if (name == "line") {
|
||||
i32 line = bd->token.pos.line;
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
@@ -9821,7 +9834,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
if (tav.mode != Addressing_Constant) {
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(tav.value.kind == ExactValue_Integer);
|
||||
if (tav.value.kind != ExactValue_Integer) {
|
||||
continue;
|
||||
}
|
||||
i64 v = big_int_to_i64(&tav.value.value_integer);
|
||||
i64 lower = bt->BitSet.lower;
|
||||
u64 index = cast(u64)(v-lower);
|
||||
|
||||
@@ -1302,6 +1302,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
||||
array_init(&i->init_procedures, a, 0, 0);
|
||||
array_init(&i->fini_procedures, a, 0, 0);
|
||||
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
|
||||
array_init(&i->defineables, a);
|
||||
|
||||
map_init(&i->objc_msgSend_types);
|
||||
string_map_init(&i->load_file_cache);
|
||||
@@ -1331,6 +1332,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
|
||||
string_map_destroy(&i->packages);
|
||||
array_free(&i->variable_init_order);
|
||||
array_free(&i->required_foreign_imports_through_force);
|
||||
array_free(&i->defineables);
|
||||
|
||||
mpsc_destroy(&i->entity_queue);
|
||||
mpsc_destroy(&i->definition_queue);
|
||||
@@ -4110,6 +4112,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
bool is_test = false;
|
||||
bool is_init = false;
|
||||
bool is_fini = false;
|
||||
bool is_priv = false;
|
||||
|
||||
for_array(i, vd->attributes) {
|
||||
Ast *attr = vd->attributes[i];
|
||||
@@ -4154,6 +4157,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
}
|
||||
if (!success) {
|
||||
error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
|
||||
} else {
|
||||
is_priv = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4175,6 +4180,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_priv && is_test) {
|
||||
error(decl, "Attribute 'private' is not allowed on a test case");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity_visibility_kind == EntityVisiblity_Public &&
|
||||
(c->scope->flags&ScopeFlag_File) &&
|
||||
c->scope->file) {
|
||||
|
||||
@@ -382,6 +382,17 @@ struct GenTypesData {
|
||||
RecursiveMutex mutex;
|
||||
};
|
||||
|
||||
struct Defineable {
|
||||
String name;
|
||||
ExactValue default_value;
|
||||
TokenPos pos;
|
||||
CommentGroup *docs;
|
||||
|
||||
// These strings are only computed from previous fields when defineables are being shown or exported.
|
||||
String default_value_str;
|
||||
String pos_str;
|
||||
};
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
Checker *checker;
|
||||
@@ -408,6 +419,9 @@ struct CheckerInfo {
|
||||
Array<Entity *> entities;
|
||||
Array<Entity *> required_foreign_imports_through_force;
|
||||
|
||||
BlockingMutex defineables_mutex;
|
||||
Array<Defineable> defineables;
|
||||
|
||||
|
||||
// Below are accessed within procedures
|
||||
RwMutex global_untyped_mutex;
|
||||
|
||||
@@ -192,6 +192,7 @@ BuiltinProc__simd_end,
|
||||
|
||||
// Platform specific intrinsics
|
||||
BuiltinProc_syscall,
|
||||
BuiltinProc_syscall_bsd,
|
||||
|
||||
BuiltinProc_x86_cpuid,
|
||||
BuiltinProc_x86_xgetbv,
|
||||
@@ -512,7 +513,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
+6
-2
@@ -265,16 +265,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
if (!build_context.use_lld) { // msvc
|
||||
String res_path = {};
|
||||
defer (gb_free(heap_allocator(), res_path.text));
|
||||
|
||||
// TODO(Jeroen): Add ability to reuse .res file instead of recompiling, if `-resource:file.res` is given.
|
||||
if (build_context.has_resource) {
|
||||
String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||
res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
|
||||
gb_free(heap_allocator(), temp_res_path.text);
|
||||
|
||||
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
String temp_rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
String rc_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_rc_path, str_lit("\""));
|
||||
gb_free(heap_allocator(), temp_rc_path.text);
|
||||
defer (gb_free(heap_allocator(), rc_path.text));
|
||||
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
|
||||
"\"%.*src.exe\" /nologo /fo %.*s %.*s",
|
||||
LIT(windows_sdk_bin_path),
|
||||
LIT(res_path),
|
||||
LIT(rc_path)
|
||||
|
||||
+134
-44
@@ -2747,26 +2747,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
{
|
||||
GB_ASSERT(arg_count <= 7);
|
||||
|
||||
// FreeBSD additionally clobbers r8, r9, r10, but they
|
||||
// can also be used to pass in arguments, so this needs
|
||||
// to be handled in two parts.
|
||||
bool clobber_arg_regs[7] = {
|
||||
false, false, false, false, false, false, false
|
||||
};
|
||||
if (build_context.metrics.os == TargetOs_freebsd) {
|
||||
clobber_arg_regs[4] = true; // r10
|
||||
clobber_arg_regs[5] = true; // r8
|
||||
clobber_arg_regs[6] = true; // r9
|
||||
}
|
||||
|
||||
char asm_string[] = "syscall";
|
||||
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
|
||||
for (unsigned i = 0; i < arg_count; i++) {
|
||||
if (!clobber_arg_regs[i]) {
|
||||
constraints = gb_string_appendc(constraints, ",{");
|
||||
} else {
|
||||
constraints = gb_string_appendc(constraints, ",+{");
|
||||
}
|
||||
constraints = gb_string_appendc(constraints, ",{");
|
||||
static char const *regs[] = {
|
||||
"rax",
|
||||
"rdi",
|
||||
@@ -2790,36 +2774,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
// Some but not all system calls will additionally
|
||||
// clobber memory.
|
||||
//
|
||||
// As a fix for CVE-2019-5595, FreeBSD started
|
||||
// clobbering R8, R9, and R10, instead of restoring
|
||||
// them. Additionally unlike Linux, instead of
|
||||
// returning negative errno, positive errno is
|
||||
// returned and CF is set.
|
||||
//
|
||||
// TODO:
|
||||
// * Figure out what Darwin does.
|
||||
// * Add some extra handling to propagate CF back
|
||||
// up to the caller on FreeBSD systems so that
|
||||
// the caller knows that the return value is
|
||||
// positive errno.
|
||||
constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
|
||||
if (build_context.metrics.os == TargetOs_freebsd) {
|
||||
// Second half of dealing with FreeBSD's system
|
||||
// call semantics. Explicitly clobber the registers
|
||||
// that were not used to pass in arguments, and
|
||||
// then clobber RFLAGS.
|
||||
if (arg_count < 5) {
|
||||
constraints = gb_string_appendc(constraints, ",~{r10}");
|
||||
}
|
||||
if (arg_count < 6) {
|
||||
constraints = gb_string_appendc(constraints, ",~{r8}");
|
||||
}
|
||||
if (arg_count < 7) {
|
||||
constraints = gb_string_appendc(constraints, ",~{r9}");
|
||||
}
|
||||
constraints = gb_string_appendc(constraints, ",~{cc}");
|
||||
}
|
||||
|
||||
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||
}
|
||||
break;
|
||||
@@ -2927,6 +2884,139 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
res.type = t_uintptr;
|
||||
return res;
|
||||
}
|
||||
case BuiltinProc_syscall_bsd:
|
||||
{
|
||||
// This is a BSD-style syscall where errors are indicated by a high
|
||||
// Carry Flag and a positive return value, allowing the kernel to
|
||||
// return any value that fits into a machine word.
|
||||
//
|
||||
// This is unlike Linux, where errors are indicated by a negative
|
||||
// return value, limiting what can be expressed in one result.
|
||||
unsigned arg_count = cast(unsigned)ce->args.count;
|
||||
LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count);
|
||||
for_array(i, ce->args) {
|
||||
lbValue arg = lb_build_expr(p, ce->args[i]);
|
||||
arg = lb_emit_conv(p, arg, t_uintptr);
|
||||
args[i] = arg.value;
|
||||
}
|
||||
|
||||
LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr);
|
||||
LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count);
|
||||
for (unsigned i = 0; i < arg_count; i++) {
|
||||
llvm_arg_types[i] = llvm_uintptr;
|
||||
}
|
||||
|
||||
LLVMTypeRef *results = gb_alloc_array(permanent_allocator(), LLVMTypeRef, 2);
|
||||
results[0] = lb_type(p->module, t_uintptr);
|
||||
results[1] = lb_type(p->module, t_bool);
|
||||
LLVMTypeRef llvm_results = LLVMStructTypeInContext(p->module->ctx, results, 2, false);
|
||||
|
||||
LLVMTypeRef func_type = LLVMFunctionType(llvm_results, llvm_arg_types, arg_count, false);
|
||||
|
||||
LLVMValueRef inline_asm = nullptr;
|
||||
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_amd64:
|
||||
{
|
||||
GB_ASSERT(arg_count <= 7);
|
||||
|
||||
char asm_string[] = "syscall; setnb %cl";
|
||||
|
||||
// Using CL as an output; RCX doesn't need to get clobbered later.
|
||||
gbString constraints = gb_string_make(heap_allocator(), "={rax},={cl}");
|
||||
for (unsigned i = 0; i < arg_count; i++) {
|
||||
constraints = gb_string_appendc(constraints, ",{");
|
||||
static char const *regs[] = {
|
||||
"rax",
|
||||
"rdi",
|
||||
"rsi",
|
||||
"rdx",
|
||||
"r10",
|
||||
"r8",
|
||||
"r9",
|
||||
};
|
||||
constraints = gb_string_appendc(constraints, regs[i]);
|
||||
constraints = gb_string_appendc(constraints, "}");
|
||||
}
|
||||
|
||||
// NOTE(Feoramund): If you're experiencing instability
|
||||
// regarding syscalls during optimized builds, it is
|
||||
// possible that the ABI has changed for your platform,
|
||||
// or I've missed a register clobber.
|
||||
//
|
||||
// Documentation on this topic is sparse, but I was able to
|
||||
// determine what registers were being clobbered by adding
|
||||
// dummy values to them, setting a breakpoint after the
|
||||
// syscall, and checking the state of the registers afterwards.
|
||||
//
|
||||
// Be advised that manually stepping through a debugger may
|
||||
// cause the kernel to not return via sysret, which will
|
||||
// preserve register state that normally would've been
|
||||
// otherwise clobbered.
|
||||
//
|
||||
// It is also possible that some syscalls clobber different registers.
|
||||
|
||||
if (build_context.metrics.os == TargetOs_freebsd) {
|
||||
// As a fix for CVE-2019-5595, FreeBSD started
|
||||
// clobbering R8, R9, and R10, instead of restoring
|
||||
// them.
|
||||
//
|
||||
// More info here:
|
||||
//
|
||||
// https://www.freebsd.org/security/advisories/FreeBSD-SA-19:01.syscall.asc
|
||||
// https://github.com/freebsd/freebsd-src/blob/098dbd7ff7f3da9dda03802cdb2d8755f816eada/sys/amd64/amd64/exception.S#L605
|
||||
// https://stackoverflow.com/q/66878250
|
||||
constraints = gb_string_appendc(constraints, ",~{r8},~{r9},~{r10}");
|
||||
}
|
||||
|
||||
// Both FreeBSD and NetBSD might clobber RDX.
|
||||
//
|
||||
// For NetBSD, it was clobbered during a call to sysctl.
|
||||
//
|
||||
// For FreeBSD, it's listed as "return value 2" in their
|
||||
// AMD64 assembly, so there's no guarantee that it will persist.
|
||||
constraints = gb_string_appendc(constraints, ",~{rdx},~{r11},~{cc},~{memory}");
|
||||
|
||||
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||
}
|
||||
break;
|
||||
case TargetArch_arm64:
|
||||
{
|
||||
GB_ASSERT(arg_count <= 7);
|
||||
|
||||
char asm_string[] = "svc #0; cset x8, cc";
|
||||
gbString constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
|
||||
for (unsigned i = 0; i < arg_count; i++) {
|
||||
constraints = gb_string_appendc(constraints, ",{");
|
||||
static char const *regs[] = {
|
||||
"x8",
|
||||
"x0",
|
||||
"x1",
|
||||
"x2",
|
||||
"x3",
|
||||
"x4",
|
||||
"x5",
|
||||
};
|
||||
constraints = gb_string_appendc(constraints, regs[i]);
|
||||
constraints = gb_string_appendc(constraints, "}");
|
||||
}
|
||||
|
||||
// FreeBSD clobbered x1 on a call to sysctl.
|
||||
constraints = gb_string_appendc(constraints, ",~{x1},~{cc},~{memory}");
|
||||
|
||||
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Unsupported platform");
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall2(p->builder, func_type, inline_asm, args, arg_count, "");
|
||||
res.type = make_optional_ok_type(t_uintptr, true);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_objc_send:
|
||||
return lb_handle_objc_send(p, expr);
|
||||
|
||||
+156
@@ -288,6 +288,9 @@ enum BuildFlagKind {
|
||||
BuildFlag_NoThreadedChecker,
|
||||
BuildFlag_ShowDebugMessages,
|
||||
|
||||
BuildFlag_ShowDefineables,
|
||||
BuildFlag_ExportDefineables,
|
||||
|
||||
BuildFlag_Vet,
|
||||
BuildFlag_VetShadowing,
|
||||
BuildFlag_VetUnused,
|
||||
@@ -483,6 +486,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_ShowDefineables, str_lit("show-defineables"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_ExportDefineables, str_lit("export-defineables"), BuildFlagParam_String, Command__does_check);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
|
||||
@@ -814,6 +820,24 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
break;
|
||||
}
|
||||
case BuildFlag_ShowDefineables: {
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
build_context.show_defineables = true;
|
||||
break;
|
||||
}
|
||||
case BuildFlag_ExportDefineables: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
|
||||
String export_path = string_trim_whitespace(value.value_string);
|
||||
if (is_build_flag_path_valid(export_path)) {
|
||||
build_context.export_defineables_file = path_to_full_path(heap_allocator(), export_path);
|
||||
} else {
|
||||
gb_printf_err("Invalid -export-defineables path, got %.*s\n", LIT(export_path));
|
||||
bad_flags = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BuildFlag_ShowSystemCalls: {
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
build_context.show_system_calls = true;
|
||||
@@ -1553,6 +1577,115 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin
|
||||
gb_printf("Done.\n");
|
||||
}
|
||||
|
||||
gb_internal void check_defines(BuildContext *bc, Checker *c) {
|
||||
for (auto const &entry : bc->defined_values) {
|
||||
String name = make_string_c(entry.key);
|
||||
ExactValue value = entry.value;
|
||||
GB_ASSERT(value.kind != ExactValue_Invalid);
|
||||
|
||||
bool found = false;
|
||||
for_array(i, c->info.defineables) {
|
||||
Defineable *def = &c->info.defineables[i];
|
||||
if (def->name == name) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ERROR_BLOCK();
|
||||
warning(nullptr, "given -define:%.*s is unused in the project", LIT(name));
|
||||
error_line("\tSuggestion: use the -show-defineables flag for an overview of the possible defines\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void temp_alloc_defineable_strings(Checker *c) {
|
||||
for_array(i, c->info.defineables) {
|
||||
Defineable *def = &c->info.defineables[i];
|
||||
def->default_value_str = make_string_c(write_exact_value_to_string(gb_string_make(temporary_allocator(), ""), def->default_value));
|
||||
def->pos_str = make_string_c(token_pos_to_string(def->pos));
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal GB_COMPARE_PROC(defineables_cmp) {
|
||||
Defineable *x = (Defineable *)a;
|
||||
Defineable *y = (Defineable *)b;
|
||||
|
||||
int cmp = 0;
|
||||
|
||||
String x_file = get_file_path_string(x->pos.file_id);
|
||||
String y_file = get_file_path_string(y->pos.file_id);
|
||||
cmp = string_compare(x_file, y_file);
|
||||
if (cmp) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return i32_cmp(x->pos.offset, y->pos.offset);
|
||||
}
|
||||
|
||||
gb_internal void sort_defineables(Checker *c) {
|
||||
gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp);
|
||||
}
|
||||
|
||||
gb_internal void export_defineables(Checker *c, String path) {
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, (char *)path.text);
|
||||
if (err != gbFileError_None) {
|
||||
gb_printf_err("Failed to export defineables to: %.*s\n", LIT(path));
|
||||
gb_exit(1);
|
||||
return;
|
||||
} else {
|
||||
gb_printf("Exporting defineables to '%.*s'...\n", LIT(path));
|
||||
}
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
gbString docs = gb_string_make(heap_allocator(), "");
|
||||
defer (gb_string_free(docs));
|
||||
|
||||
gb_fprintf(&f, "Defineable,Default Value,Docs,Location\n");
|
||||
for_array(i, c->info.defineables) {
|
||||
Defineable *def = &c->info.defineables[i];
|
||||
|
||||
gb_string_clear(docs);
|
||||
if (def->docs) {
|
||||
docs = gb_string_appendc(docs, "\"");
|
||||
for (Token const &token : def->docs->list) {
|
||||
for (isize i = 0; i < token.string.len; i++) {
|
||||
u8 c = token.string.text[i];
|
||||
if (c == '"') {
|
||||
docs = gb_string_appendc(docs, "\"\"");
|
||||
} else {
|
||||
docs = gb_string_append_length(docs, &c, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
docs = gb_string_appendc(docs, "\"");
|
||||
}
|
||||
|
||||
gb_fprintf(&f,"%.*s,%.*s,%s,%.*s\n", LIT(def->name), LIT(def->default_value_str), docs, LIT(def->pos_str));
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void show_defineables(Checker *c) {
|
||||
for_array(i, c->info.defineables) {
|
||||
Defineable *def = &c->info.defineables[i];
|
||||
if (has_ansi_terminal_colours()) {
|
||||
gb_printf("\x1b[0;90m");
|
||||
}
|
||||
printf("%.*s\n", LIT(def->pos_str));
|
||||
if (def->docs) {
|
||||
for (Token const &token : def->docs->list) {
|
||||
gb_printf("%.*s\n", LIT(token.string));
|
||||
}
|
||||
}
|
||||
if (has_ansi_terminal_colours()) {
|
||||
gb_printf("\x1b[0m");
|
||||
}
|
||||
gb_printf("%.*s :: %.*s\n\n", LIT(def->name), LIT(def->default_value_str));
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void show_timings(Checker *c, Timings *t) {
|
||||
Parser *p = c->parser;
|
||||
isize lines = p->total_line_count;
|
||||
@@ -1955,6 +2088,15 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(2, "Usage in code:");
|
||||
print_usage_line(3, "#config(SPAM, default_value)");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-show-defineables");
|
||||
print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project.");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-export-defineables:<filename>");
|
||||
print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path.");
|
||||
print_usage_line(2, "Example: -export-defineables:defineables.csv");
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
if (build) {
|
||||
@@ -2953,6 +3095,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
defer (destroy_checker(checker));
|
||||
|
||||
check_parsed_files(checker);
|
||||
check_defines(&build_context, checker);
|
||||
if (any_errors()) {
|
||||
print_all_errors();
|
||||
return 1;
|
||||
@@ -2961,6 +3104,19 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
print_all_errors();
|
||||
}
|
||||
|
||||
if (build_context.show_defineables || build_context.export_defineables_file != "") {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
temp_alloc_defineable_strings(checker);
|
||||
sort_defineables(checker);
|
||||
|
||||
if (build_context.show_defineables) {
|
||||
show_defineables(checker);
|
||||
}
|
||||
|
||||
if (build_context.export_defineables_file != "") {
|
||||
export_defineables(checker, build_context.export_defineables_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.command_kind == Command_strip_semicolon) {
|
||||
return strip_semicolons(parser);
|
||||
|
||||
Reference in New Issue
Block a user