mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Panic function that may be redefined. Trap and stacktrace builtins. Bug using builtin fixed. Fixes to using $$LINE and friends. Produces a stacktrace on error.
This commit is contained in:
committed by
Christoffer Lerno
parent
3490814d73
commit
6789fab93c
@@ -10,9 +10,44 @@ macro scope(&variable; @body) @autoimport
|
||||
@body();
|
||||
}
|
||||
|
||||
extern fn void printf(char*, ...);
|
||||
|
||||
struct CallstackElement
|
||||
{
|
||||
CallstackElement* prev;
|
||||
char* function;
|
||||
char* file;
|
||||
uint line;
|
||||
}
|
||||
fn void panic(char* message, char *file, char *function, uint line) @autoimport
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
|
||||
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), "\nERROR: '%s'\n", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), "\nERROR: '%s', function %s (%s:%d)\n", message, function, file, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line);
|
||||
stack = stack.prev;
|
||||
}
|
||||
|
||||
$endif;
|
||||
|
||||
$$trap();
|
||||
}
|
||||
|
||||
macro unreachable($string = "Unreachable statement reached.") @autoimport @noreturn
|
||||
{
|
||||
assert(false, $string);
|
||||
panic($string, $$FILE, $$FUNC, $$LINE);
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,6 +26,5 @@ struct VarArrayHeader
|
||||
usize size;
|
||||
usize capacity;
|
||||
void *allocator;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -540,6 +540,12 @@ static void parse_option(BuildOptions *options)
|
||||
options->no_stdlib = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("panicfn"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --panicfn needs a function name.");
|
||||
options->panicfn = next_arg();
|
||||
return;
|
||||
}
|
||||
if (match_longopt("lib"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --lib needs a directory.");
|
||||
|
||||
@@ -227,6 +227,7 @@ typedef struct BuildOptions_
|
||||
bool emit_bitcode;
|
||||
bool test_mode;
|
||||
bool no_stdlib;
|
||||
const char *panicfn;
|
||||
RelocModel reloc_model;
|
||||
X86VectorCapability x86_vector_capability;
|
||||
bool print_keywords;
|
||||
@@ -278,6 +279,7 @@ typedef struct
|
||||
CompilerBackend backend;
|
||||
uint32_t symtab_size;
|
||||
uint32_t switchrange_max_size;
|
||||
const char *panicfn;
|
||||
const char *cc;
|
||||
const char *cflags;
|
||||
const char **csource_dirs;
|
||||
|
||||
@@ -163,6 +163,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
}
|
||||
target->no_stdlib = options->no_stdlib;
|
||||
target->emit_llvm = options->emit_llvm;
|
||||
target->panicfn = options->panicfn;
|
||||
if (options->x86_vector_capability != X86VECTOR_DEFAULT)
|
||||
{
|
||||
target->feature.x86_vector_capability = options->x86_vector_capability;
|
||||
|
||||
@@ -120,7 +120,9 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
|
||||
const char *cpu = get_valid_string(json, "cpu", type, false);
|
||||
int reloc = get_valid_string_setting(json, "reloc", type, reloc_models, 0, 5, "'none', 'pic', 'PIC', 'pie' or 'PIE'.");
|
||||
int x86vec = get_valid_string_setting(json, "x86vec", type, vector_capability, 0, 5, "none, mmx, sse, avx or avx512");
|
||||
const char *panicfn = get_valid_string(json, "panicfn", type, false);
|
||||
|
||||
target->panicfn = panicfn;
|
||||
if (cc) target->cc = cc;
|
||||
if (cflags) target->cflags = cflags;
|
||||
if (csource_dirs) target->csource_dirs = csource_dirs;
|
||||
@@ -156,6 +158,7 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
|
||||
// Use the fact that they correspond to 0, 1, -1
|
||||
target->feature.x86_struct_return = get_valid_bool(json, "x86-stack-struct-return", type, target->feature.x86_struct_return);
|
||||
target->feature.soft_float = get_valid_bool(json, "soft-float", type, target->feature.soft_float);
|
||||
target->no_stdlib = get_valid_bool(json, "nostdlib", type, false);
|
||||
|
||||
}
|
||||
static void project_add_target(Project *project, BuildTarget *default_target, JSONObject *json, const char *name, const char *type)
|
||||
|
||||
@@ -1465,6 +1465,7 @@ typedef struct
|
||||
DeclTable symbols;
|
||||
DeclTable generic_symbols;
|
||||
Path std_module_path;
|
||||
Decl *panic_fn;
|
||||
} GlobalContext;
|
||||
|
||||
|
||||
@@ -1645,21 +1646,6 @@ extern const char *kw_LINE;
|
||||
extern const char *kw_LINEREAL;
|
||||
extern const char *kw_incr;
|
||||
extern const char *kw_check_assign;
|
||||
extern const char *kw_builtin_ceil;
|
||||
extern const char *kw_builtin_trunc;
|
||||
extern const char *kw_builtin_sqrt;
|
||||
extern const char *kw_builtin_cos;
|
||||
extern const char *kw_builtin_sin;
|
||||
extern const char *kw_builtin_log;
|
||||
extern const char *kw_builtin_log2;
|
||||
extern const char *kw_builtin_log10;
|
||||
extern const char *kw_builtin_max;
|
||||
extern const char *kw_builtin_min;
|
||||
extern const char *kw_builtin_pow;
|
||||
extern const char *kw_builtin_exp;
|
||||
extern const char *kw_builtin_fabs;
|
||||
extern const char *kw_builtin_fma;
|
||||
extern const char *kw_builtin_cmpxchg;
|
||||
extern const char *kw_argc;
|
||||
extern const char *kw_argv;
|
||||
extern const char *kw_mainstub;
|
||||
@@ -1675,6 +1661,11 @@ INLINE Expr *exprptrzero(ExprId id)
|
||||
return id ? exprptr(id) : NULL;
|
||||
}
|
||||
|
||||
INLINE Type *typeinfotype(TypeInfoId id_)
|
||||
{
|
||||
return type_infoptr(id_)->type;
|
||||
}
|
||||
|
||||
static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; }
|
||||
static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return false; }
|
||||
bool ast_is_not_empty(Ast *ast);
|
||||
@@ -1987,6 +1978,7 @@ bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type
|
||||
|
||||
bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable);
|
||||
Decl *sema_resolve_symbol_in_current_dynamic_scope(SemaContext *context, const char *symbol);
|
||||
Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name);
|
||||
Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name_resolve);
|
||||
Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref);
|
||||
Decl *sema_find_extension_method_in_module(Module *module, Type *type, const char *method_name);
|
||||
|
||||
@@ -133,7 +133,7 @@ void unit_register_external_symbol(CompilationUnit *unit, Decl *decl)
|
||||
|
||||
void decl_register(Decl *decl)
|
||||
{
|
||||
if (decl->visibility != VISIBLE_PUBLIC) return;
|
||||
if (decl->visibility != VISIBLE_PUBLIC && decl->visibility != VISIBLE_EXTERN) return;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_POISONED:
|
||||
|
||||
@@ -706,6 +706,9 @@ typedef enum
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BUILTIN_UNREACHABLE,
|
||||
BUILTIN_TRAP,
|
||||
BUILTIN_STACKTRACE,
|
||||
BUILTIN_CEIL,
|
||||
BUILTIN_TRUNC,
|
||||
BUILTIN_SQRT,
|
||||
|
||||
@@ -850,6 +850,13 @@ void *llvm_gen(Module *module)
|
||||
gencontext_init(gen_context, module);
|
||||
gencontext_begin_module(gen_context);
|
||||
|
||||
// Declare the panic function implicitly
|
||||
Decl *panicfn = gen_context->panicfn;
|
||||
if (panicfn && panicfn->module != module)
|
||||
{
|
||||
llvm_emit_extern_decl(gen_context, panicfn);
|
||||
}
|
||||
|
||||
VECEACH(module->units, j)
|
||||
{
|
||||
CompilationUnit *unit = module->units[j];
|
||||
@@ -857,11 +864,13 @@ void *llvm_gen(Module *module)
|
||||
gen_context->debug.compile_unit = unit->llvm.debug_compile_unit;
|
||||
gen_context->debug.file = unit->llvm.debug_file;
|
||||
|
||||
|
||||
VECEACH(unit->external_symbol_list, i)
|
||||
{
|
||||
Decl *d = unit->external_symbol_list[i];
|
||||
// Avoid duplicating symbol
|
||||
if (d->module == unit->module) continue;
|
||||
if (d == panicfn) continue;
|
||||
llvm_emit_extern_decl(gen_context, unit->external_symbol_list[i]);
|
||||
}
|
||||
VECEACH(unit->methods, i)
|
||||
|
||||
@@ -3590,8 +3590,7 @@ static inline void llvm_emit_force_unwrap_expr(GenContext *c, BEValue *be_value,
|
||||
// TODO, we should add info about the error.
|
||||
SourceSpan loc = expr->span;
|
||||
File *file = source_file_by_id(loc.file_id);
|
||||
llvm_emit_debug_output(c, "Runtime error force unwrap!", file->name, c->cur_func_decl->extname, loc.row ? loc.row : 1);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
llvm_emit_panic(c, "Runtime error force unwrap!", file->name, c->cur_func_decl->extname, loc.row ? loc.row : 1);
|
||||
LLVMBuildUnreachable(c->builder);
|
||||
c->current_block = NULL;
|
||||
c->current_block_is_target = NULL;
|
||||
@@ -4213,12 +4212,12 @@ static void llvm_emit_intrinsic_expr(GenContext *c, unsigned intrinsic, BEValue
|
||||
LLVMValueRef arg_results[4];
|
||||
for (unsigned i = 0; i < arguments; i++)
|
||||
{
|
||||
llvm_emit_expr(c, be_value, expr->call_expr.arguments[0]);
|
||||
llvm_emit_expr(c, be_value, expr->call_expr.arguments[i]);
|
||||
llvm_value_rvalue(c, be_value);
|
||||
arg_results[i] = be_value->value;
|
||||
}
|
||||
LLVMTypeRef call_type = llvm_get_type(c, expr->type);
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, &call_type, 1, arg_results, arguments);
|
||||
LLVMTypeRef call_type = expr->type == type_void ? NULL : llvm_get_type(c, expr->type);
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, &call_type, call_type ? 1 : 0, arg_results, arguments);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
@@ -4377,7 +4376,11 @@ unsigned llvm_get_intrinsic(BuiltinFunction func)
|
||||
switch (func)
|
||||
{
|
||||
case BUILTIN_NONE:
|
||||
case BUILTIN_UNREACHABLE:
|
||||
case BUILTIN_STACKTRACE:
|
||||
UNREACHABLE
|
||||
case BUILTIN_TRAP:
|
||||
return intrinsic_id.trap;
|
||||
case BUILTIN_CEIL:
|
||||
return intrinsic_id.ceil;
|
||||
case BUILTIN_TRUNC:
|
||||
@@ -4431,6 +4434,25 @@ LLVMAtomicOrdering llvm_atomic_ordering(Atomicity atomicity)
|
||||
void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
BuiltinFunction func = exprptr(expr->call_expr.function)->builtin_expr.builtin;
|
||||
if (func == BUILTIN_UNREACHABLE)
|
||||
{
|
||||
llvm_value_set(result_value, LLVMBuildUnreachable(c->builder), type_void);
|
||||
c->current_block = NULL;
|
||||
c->current_block_is_target = NULL;
|
||||
LLVMBasicBlockRef after_unreachable = llvm_basic_block_new(c, "after.unreachable");
|
||||
llvm_emit_block(c, after_unreachable);
|
||||
return;
|
||||
}
|
||||
if (func == BUILTIN_STACKTRACE)
|
||||
{
|
||||
if (!c->debug.enable_stacktrace)
|
||||
{
|
||||
llvm_value_set(result_value, llvm_get_zero(c, type_voidptr), type_voidptr);
|
||||
return;
|
||||
}
|
||||
llvm_value_set(result_value, llvm_emit_bitcast(c, c->debug.stack_slot, type_voidptr), type_voidptr);
|
||||
return;
|
||||
}
|
||||
if (func == BUILTIN_VOLATILE_STORE)
|
||||
{
|
||||
BEValue value;
|
||||
@@ -4482,11 +4504,18 @@ void llvm_add_call_attributes(GenContext *c, LLVMValueRef call_value, int start_
|
||||
}
|
||||
void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
|
||||
if (expr->call_expr.is_builtin)
|
||||
{
|
||||
llvm_emit_builtin_call(c, result_value, expr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->debug.stack_slot_row)
|
||||
{
|
||||
llvm_store(c, c->debug.stack_slot_row, llvm_const_int(c, type_uint, expr->span.row), type_abi_alignment(type_uint));
|
||||
}
|
||||
|
||||
LLVMTypeRef func_type;
|
||||
LLVMValueRef func;
|
||||
BEValue temp_value;
|
||||
|
||||
@@ -402,62 +402,98 @@ void llvm_emit_return_implicit(GenContext *c)
|
||||
llvm_emit_return_abi(c, NULL, &value);
|
||||
}
|
||||
|
||||
void llvm_emit_function_body(GenContext *context, Decl *decl)
|
||||
void llvm_emit_function_body(GenContext *c, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("Generating function %s.", decl->extname);
|
||||
assert(decl->backend_ref);
|
||||
|
||||
bool emit_debug = llvm_use_debug(context);
|
||||
LLVMValueRef prev_function = context->function;
|
||||
LLVMBuilderRef prev_builder = context->builder;
|
||||
bool emit_debug = llvm_use_debug(c);
|
||||
LLVMValueRef prev_function = c->function;
|
||||
LLVMBuilderRef prev_builder = c->builder;
|
||||
|
||||
context->error_var = NULL;
|
||||
context->catch_block = NULL;
|
||||
|
||||
context->function = decl->backend_ref;
|
||||
c->error_var = NULL;
|
||||
c->catch_block = NULL;
|
||||
|
||||
c->function = decl->backend_ref;
|
||||
if (emit_debug)
|
||||
{
|
||||
context->debug.function = LLVMGetSubprogram(context->function);
|
||||
c->debug.function = LLVMGetSubprogram(c->function);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->module->name->module);
|
||||
scratch_buffer_append("::");
|
||||
scratch_buffer_append(decl->name ? decl->name : "anon");
|
||||
c->debug.func_name = llvm_emit_zstring(c, scratch_buffer_to_string());
|
||||
|
||||
File *file = source_file_by_id(decl->span.file_id);
|
||||
c->debug.file_name = llvm_emit_zstring(c, file->name);
|
||||
}
|
||||
}
|
||||
|
||||
context->cur_func_decl = decl;
|
||||
c->cur_func_decl = decl;
|
||||
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
||||
context->current_block = entry;
|
||||
context->current_block_is_target = true;
|
||||
context->block_return_exit = NULL;
|
||||
context->in_block = 0;
|
||||
context->builder = LLVMCreateBuilderInContext(context->context);
|
||||
LLVMPositionBuilderAtEnd(context->builder, entry);
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry");
|
||||
c->current_block = entry;
|
||||
c->current_block_is_target = true;
|
||||
c->block_return_exit = NULL;
|
||||
c->in_block = 0;
|
||||
c->builder = LLVMCreateBuilderInContext(c->context);
|
||||
LLVMPositionBuilderAtEnd(c->builder, entry);
|
||||
|
||||
LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point");
|
||||
context->alloca_point = alloca_point;
|
||||
LLVMValueRef alloca_point = LLVMBuildAlloca(c->builder, LLVMInt32TypeInContext(c->context), "alloca_point");
|
||||
c->alloca_point = alloca_point;
|
||||
|
||||
FunctionPrototype *prototype = decl->type->func.prototype;
|
||||
unsigned arg = 0;
|
||||
|
||||
if (emit_debug)
|
||||
{
|
||||
llvm_debug_scope_push(context, context->debug.function);
|
||||
llvm_debug_scope_push(c, c->debug.function);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
LLVMTypeRef slot_type = c->debug.stack_type;
|
||||
LLVMTypeRef ptr_to_slot_type = LLVMPointerType(slot_type, 0);
|
||||
if (!c->debug.last_ptr)
|
||||
{
|
||||
LLVMValueRef last_stack = c->debug.last_ptr = LLVMAddGlobal(c->module, ptr_to_slot_type, ".$last_stack");
|
||||
LLVMSetThreadLocal(last_stack, true);
|
||||
LLVMSetInitializer(last_stack, LLVMConstNull(ptr_to_slot_type));
|
||||
LLVMSetVisibility(last_stack, LLVMDefaultVisibility);
|
||||
LLVMSetLinkage(last_stack, LLVMWeakODRLinkage);
|
||||
}
|
||||
AlignSize alignment = llvm_abi_alignment(c, slot_type);
|
||||
c->debug.stack_slot = llvm_emit_alloca(c, slot_type, alignment, ".$stackslot");
|
||||
AlignSize align_to_use;
|
||||
LLVMValueRef prev_ptr = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 0, alignment, &align_to_use);
|
||||
llvm_store(c, prev_ptr, LLVMBuildLoad2(c->builder, ptr_to_slot_type, c->debug.last_ptr, ""), align_to_use);
|
||||
LLVMValueRef func_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 1, alignment, &align_to_use);
|
||||
llvm_store(c, func_name, c->debug.func_name, align_to_use);
|
||||
LLVMValueRef file_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 2, alignment, &align_to_use);
|
||||
llvm_store(c, file_name, c->debug.file_name, align_to_use);
|
||||
c->debug.stack_slot_row = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 3, alignment, &align_to_use);
|
||||
llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr));
|
||||
}
|
||||
}
|
||||
|
||||
context->failable_out = NULL;
|
||||
context->return_out = NULL;
|
||||
c->failable_out = NULL;
|
||||
c->return_out = NULL;
|
||||
if (prototype->ret_abi_info->kind == ABI_ARG_INDIRECT)
|
||||
{
|
||||
if (prototype->is_failable)
|
||||
{
|
||||
context->failable_out = LLVMGetParam(context->function, arg++);
|
||||
c->failable_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->return_out = LLVMGetParam(context->function, arg++);
|
||||
c->return_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
}
|
||||
if (prototype->ret_by_ref_abi_info)
|
||||
{
|
||||
assert(!context->return_out);
|
||||
context->return_out = LLVMGetParam(context->function, arg++);
|
||||
assert(!c->return_out);
|
||||
c->return_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
|
||||
|
||||
@@ -466,49 +502,49 @@ void llvm_emit_function_body(GenContext *context, Decl *decl)
|
||||
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
||||
VECEACH(decl->func_decl.function_signature.params, i)
|
||||
{
|
||||
llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], prototype->abi_args[i], &arg, i);
|
||||
llvm_emit_parameter(c, decl->func_decl.function_signature.params[i], prototype->abi_args[i], &arg, i);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMSetCurrentDebugLocation2(context->builder, NULL);
|
||||
LLVMSetCurrentDebugLocation2(c->builder, NULL);
|
||||
|
||||
assert(decl->func_decl.body);
|
||||
AstId current = astptr(decl->func_decl.body)->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
llvm_emit_stmt(context, ast_next(¤t));
|
||||
llvm_emit_stmt(c, ast_next(¤t));
|
||||
}
|
||||
|
||||
if (context->current_block && llvm_basic_block_is_unused(context->current_block))
|
||||
if (c->current_block && llvm_basic_block_is_unused(c->current_block))
|
||||
{
|
||||
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(context->current_block);
|
||||
LLVMDeleteBasicBlock(context->current_block);
|
||||
context->current_block = prev_block;
|
||||
LLVMPositionBuilderAtEnd(context->builder, context->current_block);
|
||||
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(c->current_block);
|
||||
LLVMDeleteBasicBlock(c->current_block);
|
||||
c->current_block = prev_block;
|
||||
LLVMPositionBuilderAtEnd(c->builder, c->current_block);
|
||||
}
|
||||
// Insert a return (and defer) if needed.
|
||||
if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block))
|
||||
if (c->current_block && !LLVMGetBasicBlockTerminator(c->current_block))
|
||||
{
|
||||
llvm_emit_return_implicit(context);
|
||||
llvm_emit_return_implicit(c);
|
||||
}
|
||||
|
||||
// erase alloca point
|
||||
if (LLVMGetInstructionParent(alloca_point))
|
||||
{
|
||||
context->alloca_point = NULL;
|
||||
c->alloca_point = NULL;
|
||||
LLVMInstructionEraseFromParent(alloca_point);
|
||||
}
|
||||
|
||||
LLVMDisposeBuilder(context->builder);
|
||||
context->builder = NULL;
|
||||
LLVMDisposeBuilder(c->builder);
|
||||
c->builder = NULL;
|
||||
|
||||
if (llvm_use_debug(context))
|
||||
if (llvm_use_debug(c))
|
||||
{
|
||||
llvm_debug_scope_pop(context);
|
||||
llvm_debug_scope_pop(c);
|
||||
}
|
||||
|
||||
context->builder = prev_builder;
|
||||
context->function = prev_function;
|
||||
c->builder = prev_builder;
|
||||
c->function = prev_function;
|
||||
}
|
||||
|
||||
static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index)
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
unsigned runtime_version : 8;
|
||||
bool enable_stacktrace : 1;
|
||||
LLVMDIBuilderRef builder;
|
||||
LLVMMetadataRef file;
|
||||
LLVMMetadataRef compile_unit;
|
||||
@@ -58,6 +59,12 @@ typedef struct
|
||||
SourceSpan current_range;
|
||||
LLVMMetadataRef *lexical_block_stack;
|
||||
LLVMMetadataRef inlined_at;
|
||||
LLVMValueRef func_name;
|
||||
LLVMValueRef file_name;
|
||||
LLVMValueRef last_ptr;
|
||||
LLVMTypeRef stack_type;
|
||||
LLVMValueRef stack_slot;
|
||||
LLVMValueRef stack_slot_row;
|
||||
} DebugContext;
|
||||
|
||||
|
||||
@@ -78,6 +85,7 @@ typedef struct
|
||||
LLVMValueRef error_var;
|
||||
LLVMTypeRef bool_type;
|
||||
LLVMTypeRef byte_type;
|
||||
Decl *panicfn;
|
||||
Decl *cur_code_decl;
|
||||
Decl *cur_func_decl;
|
||||
TypeInfo *current_return_type;
|
||||
@@ -97,7 +105,6 @@ typedef struct
|
||||
BEValue retval;
|
||||
int in_block;
|
||||
bool current_block_is_target : 1;
|
||||
bool did_call_stack_save : 1;
|
||||
LLVMTypeRef type_data_definitions[TYPE_KINDS];
|
||||
SourceSpan last_emitted_loc;
|
||||
} GenContext;
|
||||
@@ -202,6 +209,7 @@ LLVMAttributeRef LLVMCreateTypeAttribute(LLVMContextRef C, unsigned KindID,
|
||||
LLVMTypeRef type_ref);
|
||||
#endif
|
||||
|
||||
|
||||
// BE value
|
||||
void llvm_value_addr(GenContext *c, BEValue *value);
|
||||
static inline bool llvm_value_is_addr(BEValue *value) { return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_FAILABLE; }
|
||||
@@ -305,11 +313,12 @@ void llvm_store_zero(GenContext *c, BEValue *ref);
|
||||
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len);
|
||||
void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment);
|
||||
void llvm_emit_stmt(GenContext *c, Ast *ast);
|
||||
LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str);
|
||||
static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value);
|
||||
void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc);
|
||||
void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc);
|
||||
void llvm_emit_ptr_from_array(GenContext *c, BEValue *value);
|
||||
void llvm_emit_debug_output(GenContext *c, const char *message, const char *file, const char *func, unsigned line);
|
||||
void llvm_emit_panic(GenContext *c, const char *message, const char *file, const char *func, unsigned line);
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable);
|
||||
void llvm_emit_return_implicit(GenContext *c);
|
||||
void llvm_emit_struct_member_ref(GenContext *c, BEValue *struct_ref, BEValue *member_ref, unsigned member_id);
|
||||
|
||||
@@ -13,6 +13,7 @@ void gencontext_begin_module(GenContext *c)
|
||||
c->ir_filename = strformat("%s.ll", result);
|
||||
c->object_filename = strformat("%s%s", result, get_object_extension());
|
||||
|
||||
c->panicfn = global_context.panic_fn;
|
||||
c->module = LLVMModuleCreateWithNameInContext(c->code_module->name->module, c->context);
|
||||
c->machine = llvm_target_machine_create();
|
||||
c->target_data = LLVMCreateTargetDataLayout(c->machine);
|
||||
@@ -60,10 +61,23 @@ void gencontext_begin_module(GenContext *c)
|
||||
global_context.type[i]->backend_debug_type = NULL;
|
||||
global_context.type[i]->backend_typeid = NULL;
|
||||
}
|
||||
if (c->panicfn) c->panicfn->backend_ref = NULL;
|
||||
|
||||
if (active_target.debug_info != DEBUG_INFO_NONE)
|
||||
{
|
||||
c->debug.runtime_version = 1;
|
||||
c->debug.builder = LLVMCreateDIBuilder(c->module);
|
||||
if (active_target.debug_info == DEBUG_INFO_FULL && active_target.feature.safe_mode)
|
||||
{
|
||||
c->debug.stack_type = LLVMStructCreateNamed(c->context, ".$callstack");
|
||||
LLVMTypeRef types[4] = { LLVMPointerType(c->debug.stack_type, 0),
|
||||
LLVMPointerType(c->byte_type, 0),
|
||||
LLVMPointerType(c->byte_type, 0),
|
||||
llvm_get_type(c, type_uint) };
|
||||
LLVMStructSetBody(c->debug.stack_type, types, 4, false);
|
||||
c->debug.last_ptr = NULL;
|
||||
c->debug.enable_stacktrace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -420,7 +420,8 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast)
|
||||
{
|
||||
SourceSpan loc = ast->span;
|
||||
File *file = source_file_by_id(loc.file_id);
|
||||
llvm_emit_debug_output(c, "Infinite loop found", file->name, c->cur_func_decl->extname, loc.row ? loc.row : 1);
|
||||
|
||||
llvm_emit_panic(c, "Infinite loop found", file->name, c->cur_func_decl->extname, loc.row ? loc.row : 1);
|
||||
LLVMBuildUnreachable(c->builder);
|
||||
LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block");
|
||||
c->current_block = NULL;
|
||||
@@ -928,19 +929,6 @@ static inline void llvm_emit_assume(GenContext *c, Expr *expr)
|
||||
static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast)
|
||||
{
|
||||
ExprId exprid = ast->assert_stmt.expr;
|
||||
if (!exprid)
|
||||
{
|
||||
File *file = source_file_by_id(ast->span.file_id);
|
||||
unsigned row = ast->span.row;
|
||||
llvm_emit_debug_output(c, "Unreachable statement reached.", file->name, c->cur_func_decl->extname, row ? row : 1);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
LLVMBuildUnreachable(c->builder);
|
||||
LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block");
|
||||
c->current_block = NULL;
|
||||
c->current_block_is_target = false;
|
||||
llvm_emit_block(c, block);
|
||||
return;
|
||||
}
|
||||
Expr *assert_expr = exprptr(exprid);
|
||||
|
||||
if (active_target.feature.safe_mode)
|
||||
@@ -964,8 +952,7 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast)
|
||||
error = "Assert violation";
|
||||
}
|
||||
File *file = source_file_by_id(loc.file_id);
|
||||
llvm_emit_debug_output(c, error, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
llvm_emit_panic(c, error, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1);
|
||||
llvm_emit_br(c, on_ok);
|
||||
llvm_emit_block(c, on_ok);
|
||||
}
|
||||
@@ -1032,7 +1019,7 @@ void gencontext_emit_expr_stmt(GenContext *c, Ast *ast)
|
||||
llvm_emit_expr(c, &value, ast->expr_stmt);
|
||||
}
|
||||
|
||||
static LLVMValueRef llvm_emit_string(GenContext *c, const char *str)
|
||||
LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str)
|
||||
{
|
||||
LLVMTypeRef char_type = llvm_get_type(c, type_char);
|
||||
unsigned len = (unsigned)strlen(str);
|
||||
@@ -1047,129 +1034,30 @@ static LLVMValueRef llvm_emit_string(GenContext *c, const char *str)
|
||||
1, &alignment);
|
||||
return LLVMBuildBitCast(c->builder, string, LLVMPointerType(char_type, 0), "");
|
||||
}
|
||||
void llvm_emit_debug_output(GenContext *c, const char *message, const char *file, const char *func, unsigned line)
|
||||
|
||||
|
||||
void llvm_emit_panic(GenContext *c, const char *message, const char *file, const char *func, unsigned line)
|
||||
{
|
||||
if (c->debug.stack_slot_row)
|
||||
{
|
||||
llvm_store(c, c->debug.stack_slot_row, llvm_const_int(c, type_uint, line), type_abi_alignment(type_uint));
|
||||
}
|
||||
|
||||
Decl *panicfn = c->panicfn;
|
||||
if (!panicfn)
|
||||
{
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
return;
|
||||
}
|
||||
LLVMTypeRef char_ptr_type = llvm_get_ptr_type(c, type_char);
|
||||
LLVMTypeRef cint_type = llvm_get_type(c, type_cint);
|
||||
const char *name;
|
||||
int file_index;
|
||||
int line_index;
|
||||
int expr_index;
|
||||
int func_index = -1;
|
||||
OsType os = platform_target.os;
|
||||
if (platform_target.arch == ARCH_TYPE_WASM32 || platform_target.arch == ARCH_TYPE_WASM64) os = OS_TYPE_WASI;
|
||||
switch (os)
|
||||
{
|
||||
case OS_TYPE_WIN32:
|
||||
name = "_assert";
|
||||
expr_index = 0;
|
||||
file_index = 1;
|
||||
line_index = 2;
|
||||
break;
|
||||
case OS_DARWIN_TYPES:
|
||||
name = "__assert_rtn";
|
||||
func_index = 0;
|
||||
expr_index = 3;
|
||||
file_index = 1;
|
||||
line_index = 2;
|
||||
break;
|
||||
case OS_TYPE_SOLARIS:
|
||||
name = "__assert_c99";
|
||||
expr_index = 0;
|
||||
file_index = 1;
|
||||
line_index = 2;
|
||||
func_index = 3;
|
||||
break;
|
||||
case OS_TYPE_LINUX:
|
||||
case OS_TYPE_WASI:
|
||||
name = "__assert_fail";
|
||||
expr_index = 0;
|
||||
file_index = 1;
|
||||
line_index = 2;
|
||||
func_index = 3;
|
||||
break;
|
||||
case OS_TYPE_OPENBSD:
|
||||
name = "__assert2";
|
||||
file_index = 0;
|
||||
line_index = 1;
|
||||
func_index = 2;
|
||||
expr_index = 3;
|
||||
break;
|
||||
default:
|
||||
name = "__assert";
|
||||
expr_index = 0;
|
||||
file_index = 1;
|
||||
line_index = 2;
|
||||
func_index = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
LLVMTypeRef type;
|
||||
LLVMTypeRef void_type = LLVMVoidTypeInContext(c->context);
|
||||
switch (os)
|
||||
{
|
||||
case OS_TYPE_WIN32:
|
||||
case OS_TYPE_FREE_BSD:
|
||||
case OS_TYPE_DRAGON_FLY:
|
||||
{
|
||||
LLVMTypeRef args[3] = { char_ptr_type, char_ptr_type, cint_type };
|
||||
type = LLVMFunctionType(void_type, args, 3, false);
|
||||
break;
|
||||
}
|
||||
case OS_DARWIN_TYPES:
|
||||
case OS_TYPE_WASI:
|
||||
case OS_TYPE_LINUX:
|
||||
case OS_TYPE_SOLARIS:
|
||||
{
|
||||
LLVMTypeRef args[4] = { char_ptr_type, char_ptr_type, cint_type, char_ptr_type };
|
||||
type = LLVMFunctionType(void_type, args, 4, false);
|
||||
break;
|
||||
}
|
||||
case OS_TYPE_OPENBSD:
|
||||
{
|
||||
LLVMTypeRef args[4] = { char_ptr_type, cint_type, char_ptr_type, char_ptr_type };
|
||||
type = LLVMFunctionType(void_type, args, 4, false);
|
||||
break;
|
||||
}
|
||||
case OS_TYPE_NETBSD:
|
||||
{
|
||||
LLVMTypeRef args[3] = { char_ptr_type, cint_type, char_ptr_type };
|
||||
type = LLVMFunctionType(void_type, args, 3, false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LLVMTypeRef args[3] = { char_ptr_type, char_ptr_type, cint_type };
|
||||
type = LLVMFunctionType(void_type, args, 3, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
LLVMValueRef assert_func = LLVMGetNamedFunction(c->module, name);
|
||||
if (!assert_func)
|
||||
{
|
||||
assert_func = LLVMAddFunction(c->module, name, type);
|
||||
}
|
||||
|
||||
LLVMValueRef args[4];
|
||||
|
||||
if (func_index == -1)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(file);
|
||||
scratch_buffer_append(" : ");
|
||||
scratch_buffer_append(func);
|
||||
file = scratch_buffer_to_string();
|
||||
}
|
||||
else
|
||||
{
|
||||
args[func_index] = llvm_emit_string(c, func);
|
||||
}
|
||||
args[file_index] = llvm_emit_string(c, file);
|
||||
args[expr_index] = llvm_emit_string(c, message);
|
||||
args[line_index] = llvm_const_int(c, type_cint, line);
|
||||
|
||||
LLVMBuildCall2(c->builder, type, assert_func, args, func_index > -1 ? 4 : 3, "");
|
||||
LLVMValueRef args[4] = {
|
||||
llvm_emit_zstring(c, message),
|
||||
llvm_emit_zstring(c, file),
|
||||
func ? llvm_emit_zstring(c, func) : LLVMConstNull(char_ptr_type),
|
||||
llvm_const_int(c, type_uint, line)
|
||||
};
|
||||
|
||||
LLVMBuildCall2(c->builder, llvm_get_type(c, panicfn->type), panicfn->backend_ref, args, 4, "");
|
||||
}
|
||||
|
||||
void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc)
|
||||
@@ -1180,8 +1068,7 @@ void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_na
|
||||
llvm_emit_cond_br(c, value, panic_block, ok_block);
|
||||
llvm_emit_block(c, panic_block);
|
||||
File *file = source_file_by_id(loc.file_id);
|
||||
llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc.row);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
llvm_emit_panic(c, panic_name, file->name, c->cur_func_decl->name, loc.row);
|
||||
llvm_emit_br(c, ok_block);
|
||||
llvm_emit_block(c, ok_block);
|
||||
}
|
||||
@@ -1195,8 +1082,7 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani
|
||||
llvm_value_set_bool(&be_value, value);
|
||||
llvm_emit_cond_br(c, &be_value, panic_block, ok_block);
|
||||
llvm_emit_block(c, panic_block);
|
||||
llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0);
|
||||
llvm_emit_panic(c, panic_name, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1);
|
||||
llvm_emit_br(c, ok_block);
|
||||
llvm_emit_block(c, ok_block);
|
||||
}
|
||||
|
||||
@@ -2092,7 +2092,6 @@ static inline bool parse_doc_contract(ParseContext *c, AstId **docs_ref, DocDire
|
||||
ASSIGN_EXPR_OR_RET(ast->doc_stmt.contract.decl_exprs, parse_expression_list(c, kind == DOC_DIRECTIVE_CHECKED), false);
|
||||
const char *end = start;
|
||||
while (*++end != '\n' && *end != '\0') end++;
|
||||
end--;
|
||||
if (end > c->data.lex_start) end = c->data.lex_start;
|
||||
while (end[-1] == ' ') end--;
|
||||
scratch_buffer_clear();
|
||||
|
||||
@@ -67,51 +67,18 @@ const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, Tok
|
||||
SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", expr_type);
|
||||
return ct_eval_error;
|
||||
}
|
||||
const char *string = inner->const_expr.string.chars;
|
||||
ArraySize len = inner->const_expr.string.len;
|
||||
|
||||
ArraySize path_end = 0;
|
||||
for (ArraySize i = 0; i < len; i++)
|
||||
{
|
||||
char ch = string[i];
|
||||
if (!is_alphanum_(ch))
|
||||
{
|
||||
if (ch == ':' && i > 0 && string[i + 1] == ':')
|
||||
{
|
||||
path_end = i;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(inner, "A valid name was expected here.");
|
||||
return ct_eval_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path_end > 0)
|
||||
{
|
||||
*path_ref = path_create_from_string(string, path_end, inner->span);
|
||||
string += path_end + 2;
|
||||
len -= path_end + 2;
|
||||
}
|
||||
if (len == 0)
|
||||
const char *interned_version = NULL;
|
||||
if (!splitpathref(inner->const_expr.string.chars, inner->const_expr.string.len, path_ref, &interned_version, type))
|
||||
{
|
||||
SEMA_ERROR(inner, "A valid name was expected here.");
|
||||
return ct_eval_error;
|
||||
}
|
||||
const char *interned_version = symtab_find(string, len, fnv1a(string, len), type);
|
||||
if (!interned_version)
|
||||
if (*path_ref) (*path_ref)->span = inner->span;
|
||||
if (*type == TOKEN_INVALID_TOKEN)
|
||||
{
|
||||
if (report_missing)
|
||||
{
|
||||
if (*path_ref)
|
||||
{
|
||||
SEMA_ERROR(inner, "'%s::%.*s' could not be found, did you spell it right?", (*path_ref)->module, (int)len, string);
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(inner, "'%.*s' could not be found, did you spell it right?", (int)len, string);
|
||||
}
|
||||
SEMA_ERROR(inner, "'%s' could not be found, did you spell it right?", interned_version);
|
||||
return ct_eval_error;
|
||||
}
|
||||
return NULL;
|
||||
@@ -2209,6 +2176,10 @@ static inline unsigned builtin_expected_args(BuiltinFunction func)
|
||||
{
|
||||
switch (func)
|
||||
{
|
||||
case BUILTIN_UNREACHABLE:
|
||||
case BUILTIN_TRAP:
|
||||
case BUILTIN_STACKTRACE:
|
||||
return 0;
|
||||
case BUILTIN_CEIL:
|
||||
case BUILTIN_TRUNC:
|
||||
case BUILTIN_SQRT:
|
||||
@@ -2271,6 +2242,13 @@ static inline bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *ex
|
||||
Type *rtype = NULL;
|
||||
switch (func)
|
||||
{
|
||||
case BUILTIN_UNREACHABLE:
|
||||
case BUILTIN_TRAP:
|
||||
rtype = type_void;
|
||||
break;
|
||||
case BUILTIN_STACKTRACE:
|
||||
rtype = type_voidptr;
|
||||
break;
|
||||
case BUILTIN_CEIL:
|
||||
case BUILTIN_TRUNC:
|
||||
case BUILTIN_SQRT:
|
||||
@@ -6221,7 +6199,7 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr *
|
||||
const char *string = expr->builtin_expr.ident;
|
||||
if (string == kw_FILE)
|
||||
{
|
||||
expr_rewrite_to_string(expr, context->unit->file->name);
|
||||
expr_rewrite_to_string(expr, context->compilation_unit->file->name);
|
||||
return true;
|
||||
}
|
||||
if (string == kw_FUNC)
|
||||
@@ -7314,3 +7292,59 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex
|
||||
return true;
|
||||
}
|
||||
|
||||
bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref, TokenType *type_ref)
|
||||
{
|
||||
ArraySize path_end = 0;
|
||||
*path_ref = NULL;
|
||||
for (ArraySize i = 0; i < len; i++)
|
||||
{
|
||||
char ch = string[i];
|
||||
if (!is_alphanum_(ch))
|
||||
{
|
||||
if (ch == ':' && i > 0 && string[i + 1] == ':')
|
||||
{
|
||||
path_end = i;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path_end > 0)
|
||||
{
|
||||
*path_ref = path_create_from_string(string, path_end, INVALID_SPAN);
|
||||
string += path_end + 2;
|
||||
len -= path_end + 2;
|
||||
}
|
||||
while (len > 0)
|
||||
{
|
||||
char c = string[0];
|
||||
if (c != ' ' && c != '\t') break;
|
||||
len--;
|
||||
string++;
|
||||
}
|
||||
if (len == 0) return false;
|
||||
uint32_t hash = FNV1_SEED;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
char c = string[i];
|
||||
if (!is_alphanum_(c)) return false;
|
||||
hash = FNV1a(c, hash);
|
||||
}
|
||||
*ident_ref = symtab_find(string, len, hash, type_ref);
|
||||
if (!*ident_ref)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
if (*path_ref)
|
||||
{
|
||||
scratch_buffer_append_len((*path_ref)->module, (*path_ref)->len);
|
||||
scratch_buffer_append("::");
|
||||
}
|
||||
scratch_buffer_append_len(string, len);
|
||||
*ident_ref = scratch_buffer_to_string();
|
||||
*type_ref = TOKEN_INVALID_TOKEN;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -42,6 +42,7 @@ void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast);
|
||||
void context_change_scope_for_label(SemaContext *context, Decl *label);
|
||||
void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags);
|
||||
bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement, Ast *body);
|
||||
bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref, TokenType *type_ref);
|
||||
|
||||
#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->active_scope.defer_last
|
||||
#define POP_X(X) context->X##_target = _old_##X; context->X##_defer = _old_##X##_defer
|
||||
|
||||
@@ -104,6 +104,17 @@ static inline bool sema_is_path_found(Module **modules, Path *path, bool want_ge
|
||||
return false;
|
||||
}
|
||||
|
||||
Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name)
|
||||
{
|
||||
bool path_found = false;
|
||||
VECEACH(module_list, i)
|
||||
{
|
||||
Module *module = module_list[i];
|
||||
Decl *decl = sema_find_decl_in_module(module, path, interned_name, &path_found);
|
||||
if (decl) return decl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, NameResolve *name_resolve, bool want_generic)
|
||||
{
|
||||
const char *symbol = name_resolve->symbol;
|
||||
|
||||
@@ -2159,8 +2159,6 @@ bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
statement->assert_stmt.expr = 0;
|
||||
context->active_scope.jump_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +318,41 @@ void sema_analysis_run(void)
|
||||
{
|
||||
sema_analyze_to_stage(stage);
|
||||
}
|
||||
|
||||
if (active_target.panicfn || !active_target.no_stdlib)
|
||||
{
|
||||
const char *panicfn = active_target.panicfn ? active_target.panicfn : "std::builtin::panic";
|
||||
Path *path;
|
||||
const char *ident;
|
||||
TokenType type;
|
||||
if (!splitpathref(panicfn, strlen(panicfn), &path, &ident, &type) || path == NULL)
|
||||
{
|
||||
error_exit("'%s' is not a valid panic function.", panicfn);
|
||||
}
|
||||
Decl *decl = sema_find_decl_in_modules(global_context.module_list, path, ident);
|
||||
if (!decl)
|
||||
{
|
||||
error_exit("Panic function '%s::%s' could not be found.", path->module, ident);
|
||||
}
|
||||
if (decl->decl_kind != DECL_FUNC)
|
||||
{
|
||||
error_exit("'%s::%s' is not a function.", path->module, ident);
|
||||
}
|
||||
Decl **params = decl->func_decl.function_signature.params;
|
||||
if (vec_size(params) != 4 || params[0]->type != type_get_ptr(type_char)
|
||||
|| params[1]->type != type_get_ptr(type_char)
|
||||
|| params[2]->type != type_get_ptr(type_char)
|
||||
|| params[3]->type != type_uint
|
||||
|| typeinfotype(decl->func_decl.function_signature.returntype) != type_void)
|
||||
{
|
||||
error_exit("Expected panic function to have the signature fn void(char*, char*, uint, uint).");
|
||||
}
|
||||
global_context.panic_fn = decl;
|
||||
}
|
||||
else
|
||||
{
|
||||
global_context.panic_fn = NULL;
|
||||
}
|
||||
compiler_sema_time = bench_mark();
|
||||
|
||||
}
|
||||
|
||||
@@ -83,21 +83,6 @@ const char *kw_LINE;
|
||||
const char *kw_LINEREAL;
|
||||
const char *kw_incr;
|
||||
const char *kw_check_assign;
|
||||
const char *kw_builtin_ceil;
|
||||
const char *kw_builtin_trunc;
|
||||
const char *kw_builtin_sqrt;
|
||||
const char *kw_builtin_cos;
|
||||
const char *kw_builtin_sin;
|
||||
const char *kw_builtin_log;
|
||||
const char *kw_builtin_log2;
|
||||
const char *kw_builtin_log10;
|
||||
const char *kw_builtin_max;
|
||||
const char *kw_builtin_min;
|
||||
const char *kw_builtin_pow;
|
||||
const char *kw_builtin_exp;
|
||||
const char *kw_builtin_fabs;
|
||||
const char *kw_builtin_fma;
|
||||
const char *kw_builtin_cmpxchg;
|
||||
const char *kw_argc;
|
||||
const char *kw_argv;
|
||||
const char *kw_mainstub;;
|
||||
@@ -140,8 +125,14 @@ void symtab_init(uint32_t capacity)
|
||||
}
|
||||
|
||||
// Init some constant idents
|
||||
TokenType type = TOKEN_IDENT;
|
||||
#define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type)
|
||||
TokenType type = TOKEN_CONST_IDENT;
|
||||
kw_LINE = KW_DEF("LINE");
|
||||
kw_LINEREAL = KW_DEF("LINEREAL");
|
||||
kw_FILE = KW_DEF("FILE");
|
||||
kw_FUNC = KW_DEF("FUNC");
|
||||
|
||||
type = TOKEN_IDENT;
|
||||
kw_sizeof = KW_DEF("sizeof");
|
||||
kw_in = KW_DEF("in");
|
||||
kw_out = KW_DEF("out");
|
||||
@@ -171,10 +162,6 @@ void symtab_init(uint32_t capacity)
|
||||
kw_require = KW_DEF("require");
|
||||
kw_std = KW_DEF("std");
|
||||
kw_values = KW_DEF("values");
|
||||
kw_LINE = KW_DEF("LINE");
|
||||
kw_LINEREAL = KW_DEF("LINEREAL");
|
||||
kw_FILE = KW_DEF("FILE");
|
||||
kw_FUNC = KW_DEF("FUNC");
|
||||
kw_incr = KW_DEF("incr");
|
||||
kw_check_assign = KW_DEF("check_assign");
|
||||
|
||||
@@ -182,6 +169,9 @@ void symtab_init(uint32_t capacity)
|
||||
kw_argv = KW_DEF("_$argv");
|
||||
kw_mainstub = KW_DEF("_$mainstub");
|
||||
|
||||
builtin_list[BUILTIN_TRAP] = KW_DEF("trap");
|
||||
builtin_list[BUILTIN_UNREACHABLE] = KW_DEF("unreachable");
|
||||
builtin_list[BUILTIN_STACKTRACE] = KW_DEF("stacktrace");
|
||||
builtin_list[BUILTIN_CEIL] = KW_DEF("ceil");
|
||||
builtin_list[BUILTIN_TRUNC] = KW_DEF("trunc");
|
||||
builtin_list[BUILTIN_SIN] = KW_DEF("sin");
|
||||
|
||||
@@ -263,6 +263,30 @@ static AlignSize os_arch_max_alignment_of_tls(OsType os, ArchType arch, Environm
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool os_target_use_thread_local(OsType os)
|
||||
{
|
||||
switch (os)
|
||||
{
|
||||
case OS_UNSUPPORTED:
|
||||
return false;
|
||||
case OS_TYPE_UNKNOWN:
|
||||
case OS_TYPE_NONE:
|
||||
return false;
|
||||
case OS_TYPE_FREE_BSD:
|
||||
case OS_TYPE_IOS:
|
||||
case OS_TYPE_LINUX:
|
||||
case OS_TYPE_MACOSX:
|
||||
case OS_TYPE_NETBSD:
|
||||
case OS_TYPE_OPENBSD:
|
||||
case OS_TYPE_WIN32:
|
||||
case OS_TYPE_TVOS:
|
||||
case OS_TYPE_WATCHOS:
|
||||
case OS_TYPE_WASI:
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static bool os_target_signed_c_char_type(OsType os, ArchType arch)
|
||||
{
|
||||
switch (arch)
|
||||
@@ -1293,11 +1317,10 @@ void target_setup(BuildTarget *target)
|
||||
// TLS should not be supported for:
|
||||
// ARM Cygwin
|
||||
// NVPTX
|
||||
platform_target.tls_supported = true;
|
||||
platform_target.tls_supported = os_target_use_thread_local(platform_target.os);
|
||||
platform_target.big_endian = arch_big_endian(platform_target.arch);
|
||||
platform_target.width_pointer = arch_pointer_bit_width(platform_target.os, platform_target.arch);
|
||||
platform_target.alloca_address_space = 0;
|
||||
|
||||
platform_target.object_format = object_format_from_os(platform_target.os);
|
||||
|
||||
platform_target.int128 = os_target_supports_int128(platform_target.os, platform_target.arch);
|
||||
|
||||
@@ -15,22 +15,27 @@ fn void test()
|
||||
|
||||
// #expect: unreachable.ll
|
||||
|
||||
%x = alloca i32
|
||||
%0 = call i32 @unreachable.foo()
|
||||
store i32 %0, i32* %x
|
||||
%1 = load i32, i32* %x
|
||||
%gt = icmp sgt i32 %1, 0
|
||||
br i1 %gt, label %if.then, label %if.exit
|
||||
|
||||
if.then:
|
||||
ret void
|
||||
define void @unreachable.test() #0 {
|
||||
entry:
|
||||
%x = alloca i32, align 4
|
||||
%0 = call i32 @unreachable.foo()
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = load i32, i32* %x, align 4
|
||||
%gt = icmp sgt i32 %1, 0
|
||||
br i1 %gt, label %if.then, label %if.exit
|
||||
|
||||
if.exit:
|
||||
call void @llvm.trap()
|
||||
unreachable
|
||||
if.then: ; preds = %entry
|
||||
ret void
|
||||
|
||||
if.exit: ; preds = %entry
|
||||
call void @"std::builtin.panic"(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0), i32 11)
|
||||
unreachable
|
||||
|
||||
after.unreachable: ; No predecessors!
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%add = add i32 %2, 1
|
||||
store i32 %add, i32* %x, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
unreachable_block:
|
||||
%2 = load i32, i32* %x
|
||||
%add = add i32 %2, 1
|
||||
store i32 %add, i32* %x
|
||||
ret void
|
||||
|
||||
@@ -28,31 +28,30 @@ entry:
|
||||
%sy = alloca i32, align 4
|
||||
%1 = call double @llvm.ceil.f64(double %0)
|
||||
store double %1, double* %d, align 8
|
||||
%2 = call double @llvm.maxnum.f64(double 1.000000e+00, double 1.000000e+00)
|
||||
store double %2, double* %e, align 8
|
||||
%3 = load double, double* %d, align 8
|
||||
%2 = load double, double* %d, align 8
|
||||
%3 = call double @llvm.maxnum.f64(double 1.000000e+00, double %2)
|
||||
store double %3, double* %e, align 8
|
||||
%4 = load double, double* %d, align 8
|
||||
%5 = load double, double* %d, align 8
|
||||
%6 = call double @llvm.fma.f64(double %3, double %4, double %5)
|
||||
store double %6, double* %f, align 8
|
||||
%5 = call double @llvm.fma.f64(double %4, double 2.000000e+00, double 3.000000e+00)
|
||||
store double %5, double* %f, align 8
|
||||
store i32 13, i32* %xeb, align 4
|
||||
%7 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 0
|
||||
%6 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 0
|
||||
store i32 0, i32* %6, align 4
|
||||
%7 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 1
|
||||
store i32 0, i32* %7, align 4
|
||||
%8 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 1
|
||||
%8 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
store i32 0, i32* %8, align 4
|
||||
%9 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
store i32 0, i32* %9, align 4
|
||||
%10 = load volatile i32, i32* %xeb, align 4
|
||||
store i32 %10, i32* %sy, align 4
|
||||
%11 = load i32, i32* %sy, align 4
|
||||
%add = add i32 %11, 1
|
||||
%9 = load volatile i32, i32* %xeb, align 4
|
||||
store i32 %9, i32* %sy, align 4
|
||||
%10 = load i32, i32* %sy, align 4
|
||||
%add = add i32 %10, 1
|
||||
store volatile i32 %add, i32* %xeb, align 4
|
||||
%12 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
%13 = load i32, i32* %sy, align 4
|
||||
%add1 = add i32 %13, 2
|
||||
store volatile i32 %add1, i32* %12, align 4
|
||||
%14 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
%15 = load volatile i32, i32* %14, align 4
|
||||
store i32 %15, i32* %sy, align 4
|
||||
%11 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
%12 = load i32, i32* %sy, align 4
|
||||
%add1 = add i32 %12, 2
|
||||
store volatile i32 %add1, i32* %11, align 4
|
||||
%13 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2
|
||||
%14 = load volatile i32, i32* %13, align 4
|
||||
store i32 %14, i32* %sy, align 4
|
||||
ret i32 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,11 @@ loop.body: ; preds = %loop.cond
|
||||
br label %loop.cond
|
||||
|
||||
loop.exit: ; preds = %loop.cond
|
||||
call void @__assert_rtn(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @0, i64 0, i64 0), i8* getelementptr inbounds ([22 x i8], [22 x i8]* @1, i64 0, i64 0), i32 14, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @2, i64 0, i64 0))
|
||||
call void @"std::builtin.panic"(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @0, i64 0, i64 0), i8* getelementptr inbounds ([22 x i8], [22 x i8]* @1, i64 0, i64 0), i8* getelementptr inbounds ([21 x i8], [21 x i8]* @2, i64 0, i64 0), i32 14)
|
||||
unreachable
|
||||
|
||||
unreachable_block: ; No predecessors!
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
|
||||
Reference in New Issue
Block a user