From e1b5b0b60ceaad4d44240e69072b15c2bac03122 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 28 Sep 2022 17:03:30 +0200 Subject: [PATCH] Implement static finalize / initialize. Version bump. --- src/compiler/ast.c | 6 ++ src/compiler/compiler_internal.h | 11 ++- src/compiler/context.c | 6 ++ src/compiler/copying.c | 4 + src/compiler/enums.h | 8 +- src/compiler/llvm_codegen.c | 25 ++++++ src/compiler/llvm_codegen_expr.c | 8 +- src/compiler/llvm_codegen_function.c | 89 ++++++++++++++----- src/compiler/llvm_codegen_internal.h | 11 ++- src/compiler/llvm_codegen_stmt.c | 10 +-- src/compiler/parse_global.c | 44 +++++++++ src/compiler/sema_decls.c | 79 ++++++++++++++-- src/compiler/sema_expr.c | 2 + src/compiler/sema_passes.c | 4 + src/compiler/sema_types.c | 2 + src/compiler/semantic_analyser.c | 2 + src/compiler/symtab.c | 9 +- src/compiler/target.c | 4 + src/version.h | 2 +- .../from_docs/examples_forswitch.c3t | 2 +- test/test_suite/globals/static_global.c3 | 2 +- .../initialize/initialize_bad_prio.c3 | 16 ++++ .../initialize/initialize_finalize.c3t | 62 +++++++++++++ test/test_suite/initialize/initialize_jump.c3 | 9 ++ .../initialize/initialize_parse_error.c3 | 1 + test/test_suite/initialize/initialize_prio.c3 | 3 + test/test_suite2/globals/static_global.c3 | 2 +- .../initialize/initialize_bad_prio.c3 | 16 ++++ .../initialize/initialize_finalize.c3t | 62 +++++++++++++ .../test_suite2/initialize/initialize_jump.c3 | 9 ++ .../initialize/initialize_parse_error.c3 | 1 + .../test_suite2/initialize/initialize_prio.c3 | 3 + wrapper/src/wrapper.cpp | 8 ++ 33 files changed, 478 insertions(+), 44 deletions(-) create mode 100644 test/test_suite/initialize/initialize_bad_prio.c3 create mode 100644 test/test_suite/initialize/initialize_finalize.c3t create mode 100644 test/test_suite/initialize/initialize_jump.c3 create mode 100644 test/test_suite/initialize/initialize_parse_error.c3 create mode 100644 test/test_suite/initialize/initialize_prio.c3 create mode 100644 test/test_suite2/initialize/initialize_bad_prio.c3 create mode 100644 test/test_suite2/initialize/initialize_finalize.c3t create mode 100644 test/test_suite2/initialize/initialize_jump.c3 create mode 100644 test/test_suite2/initialize/initialize_parse_error.c3 create mode 100644 test/test_suite2/initialize/initialize_prio.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 9e5548eb5..593868660 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -87,6 +87,8 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V case DECL_CT_ASSERT: case DECL_DECLARRAY: case DECL_BODYPARAM: + case DECL_INITIALIZE: + case DECL_FINALIZE: UNREACHABLE } Type *type = type_new(kind, name ? name : "$anon"); @@ -156,6 +158,10 @@ const char *decl_to_a_name(Decl *decl) return "a struct"; case DECL_UNION: return "a union"; + case DECL_INITIALIZE: + return "a static initializer"; + case DECL_FINALIZE: + return "a static finalizer"; case DECL_VAR: switch (decl->var.kind) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index eae53f1d2..f5fcf308a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -37,6 +37,7 @@ typedef uint64_t BitSize; #define MAX_BITSTRUCT 0x1000 #define MAX_MEMBERS ((MemberIndex)(((uint64_t)2) << 28)) #define MAX_ALIGNMENT ((MemberIndex)(((uint64_t)2) << 28)) +#define MAX_PRIORITY 0xFFFF #define MAX_TYPE_SIZE UINT32_MAX #define MAX_GLOBAL_DECL_STACK (65536) #define MAX_ASM_INSTRUCTION_PARAMS 6 @@ -611,6 +612,11 @@ typedef struct AstId parent; } LabelDecl; +typedef struct +{ + unsigned priority; + AstId init; +} InitializerDecl; @@ -663,6 +669,7 @@ typedef struct Decl_ Type *type; union { + InitializerDecl xxlizer; Decl** decl_list; struct { @@ -1521,6 +1528,7 @@ struct CompilationUnit_ }; Decl **ct_ifs; Decl **ct_asserts; + Decl **xxlizers; Decl **vars; Decl **macros; Decl **methods; @@ -1782,12 +1790,13 @@ extern const char *kw_std; extern const char *kw_max; extern const char *kw_min; extern const char *kw_elements; +extern const char *kw_finalize; extern const char *kw_align; - extern const char *kw_nameof; extern const char *kw_names; extern const char *kw_sizeof; extern const char *kw_in; +extern const char *kw_initialize; extern const char *kw_out; extern const char *kw_inout; extern const char *kw_deprecated; diff --git a/src/compiler/context.c b/src/compiler/context.c index 76358ebff..284bdb3e4 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -132,6 +132,8 @@ void decl_register(Decl *decl) if (decl->visibility != VISIBLE_PUBLIC && decl->visibility != VISIBLE_EXTERN) return; switch (decl->decl_kind) { + case DECL_INITIALIZE: + case DECL_FINALIZE: case DECL_POISONED: case DECL_CT_CASE: case DECL_CT_ELIF: @@ -264,6 +266,10 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) case DECL_CT_ASSERT: vec_add(unit->ct_asserts, decl); return; + case DECL_INITIALIZE: + case DECL_FINALIZE: + vec_add(unit->xxlizers, decl); + return; } DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, unit->module->name->module); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 8b3c7fd11..d5e22ab25 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -751,6 +751,10 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) { case DECL_POISONED: break; + case DECL_INITIALIZE: + case DECL_FINALIZE: + MACRO_COPY_ASTID(copy->xxlizer.init); + break; case DECL_BODYPARAM: MACRO_COPY_DECL_LIST(copy->body_params); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index eeeae7871..48b1baec7 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -137,6 +137,8 @@ typedef enum DECL_FAULT, DECL_FAULTVALUE, DECL_FUNC, + DECL_INITIALIZE, + DECL_FINALIZE, DECL_GENERIC, DECL_IMPORT, DECL_LABEL, @@ -152,7 +154,7 @@ typedef enum #define NON_TYPE_DECLS DECL_IMPORT: case DECL_MACRO: \ case DECL_DECLARRAY: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \ case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ - case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC + case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC: case DECL_INITIALIZE: case DECL_FINALIZE #define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ @@ -723,6 +725,9 @@ typedef enum ATTR_CALL = 1 << 11, ATTR_BITSTRUCT = 1 << 12, ATTR_MACRO = 1 << 13, + ATTR_INITIALIZER = 1 << 14, + ATTR_FINALIZER = 1 << 15, + ATTR_XXLIZER = ATTR_INITIALIZER | ATTR_FINALIZER } AttributeDomain; typedef enum @@ -752,6 +757,7 @@ typedef enum ATTRIBUTE_OPERATOR, ATTRIBUTE_OVERLAP, ATTRIBUTE_PACKED, + ATTRIBUTE_PRIORITY, ATTRIBUTE_PURE, ATTRIBUTE_REFLECT, ATTRIBUTE_REGCALL, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3b097fb98..f6db689e8 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -15,6 +15,7 @@ LLVMPassBuilderOptionsRef LLVMCreatePassBuilderOptions(void); void LLVMPassBuilderOptionsSetVerifyEach(LLVMPassBuilderOptionsRef Options, LLVMBool VerifyEach); void LLVMPassBuilderOptionsSetDebugLogging(LLVMPassBuilderOptionsRef Options, LLVMBool DebugLogging); void LLVMDisposePassBuilderOptions(LLVMPassBuilderOptionsRef Options); +static void llvm_emit_constructors_and_destructors(GenContext *c); const char* llvm_version = LLVM_VERSION_STRING; const char* llvm_target = LLVM_DEFAULT_TARGET_TRIPLE; @@ -80,6 +81,22 @@ LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ptr, uint return LLVMBuildMemSet(c->builder, ptr, llvm_get_zero(c, type_char), llvm_const_int(c, type_usize, size), align); } +INLINE void llvm_emit_xtor(GenContext *c, LLVMValueRef *list, const char *name) +{ + if (!list) return; + unsigned len = vec_size(list); + LLVMTypeRef type = LLVMTypeOf(list[0]); + LLVMValueRef array = LLVMConstArray(type, list, len); + LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), name); + LLVMSetLinkage(global, LLVMAppendingLinkage); + LLVMSetInitializer(global, array); +} +void llvm_emit_constructors_and_destructors(GenContext *c) +{ + llvm_emit_xtor(c, c->constructors, "llvm.global_ctors"); + llvm_emit_xtor(c, c->destructors, "llvm.global_dtors"); +} + /** * Consider the case when we have int[5] x = { [0] = 1, [1] = 3 } * In this case we want this: { i32 0, i32 2, [8 x i32] zeroinitializer } @@ -968,6 +985,8 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) case DECL_TYPEDEF: case DECL_UNION: case DECL_DECLARRAY: + case DECL_INITIALIZE: + case DECL_FINALIZE: case DECL_BODYPARAM: UNREACHABLE; } @@ -988,6 +1007,9 @@ void *llvm_gen(Module *module) gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; gen_context->debug.file = unit->llvm.debug_file; + FOREACH_BEGIN(Decl *initializer, unit->xxlizers) + llvm_emit_xxlizer(gen_context, initializer); + FOREACH_END(); VECEACH(unit->methods, i) { llvm_emit_function_decl(gen_context, unit->methods[i]); @@ -1043,6 +1065,9 @@ void *llvm_gen(Module *module) gencontext_end_file_emit(gen_context, unit); } + + llvm_emit_constructors_and_destructors(gen_context); + // EmitDeferred() if (llvm_use_debug(gen_context)) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 26887e13c..670410b59 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3575,7 +3575,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_panic(c, "Runtime error force unwrap!", file->name, c->cur_func_decl->extname, loc.row ? loc.row : 1); + llvm_emit_panic(c, "Runtime error force unwrap!", file->name, c->cur_func.name, loc.row ? loc.row : 1); LLVMBuildUnreachable(c->builder); c->current_block = NULL; c->current_block_is_target = false; @@ -5254,7 +5254,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } File *file = source_file_by_id(expr->span.file_id); - llvm_emit_panic(c, "Attempted to access 'inner' on non composite type", file->name, c->cur_func_decl->name, expr->span.row); + llvm_emit_panic(c, "Attempted to access 'inner' on non composite type", file->name, c->cur_func.name, expr->span.row); c->current_block = NULL; c->current_block_is_target = false; LLVMBuildUnreachable(c->builder); @@ -5287,7 +5287,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } File *file = source_file_by_id(expr->span.file_id); - llvm_emit_panic(c, "Attempted to access 'names' on non enum/fault type.", file->name, c->cur_func_decl->name, expr->span.row); + llvm_emit_panic(c, "Attempted to access 'names' on non enum/fault type.", file->name, c->cur_func.name, expr->span.row); c->current_block = NULL; c->current_block_is_target = false; LLVMBuildUnreachable(c->builder); @@ -5324,7 +5324,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } File *file = source_file_by_id(expr->span.file_id); - llvm_emit_panic(c, "Attempted to access 'len' on non array type", file->name, c->cur_func_decl->name, expr->span.row); + llvm_emit_panic(c, "Attempted to access 'len' on non array type", file->name, c->cur_func.name, expr->span.row); c->current_block = NULL; c->current_block_is_target = false; LLVMBuildUnreachable(c->builder); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index ecbdb9416..2f0fbad49 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -10,6 +10,9 @@ static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef valu static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index, AlignSize alignment); static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIArgInfo *info, unsigned *index); static inline void llvm_emit_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index); +static inline void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_name, + const char *function_name, + FileId file_id, FunctionPrototype *prototype, Signature *signature, Ast *body); bool llvm_emit_check_block_branch(GenContext *context) { @@ -275,7 +278,15 @@ static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef valu void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable) { - FunctionPrototype *prototype = c->cur_func_decl->type->function.prototype; + FunctionPrototype *prototype = c->cur_func.prototype; + + // If there is no prototype, this is a static initializer, so bail. + if (!prototype) + { + llvm_emit_return_value(c, NULL); + return; + } + ABIArgInfo *info = prototype->ret_abi_info; // If we have a failable it's always the return argument, so we need to copy @@ -388,7 +399,7 @@ DIRECT_RETURN: void llvm_emit_return_implicit(GenContext *c) { - Type *rtype_real = c->cur_func_decl->type->function.prototype->rtype; + Type *rtype_real = c->cur_func.prototype ? c->cur_func.prototype->rtype : type_void; if (type_lowering(type_no_optional(rtype_real)) != type_void) { LLVMBuildUnreachable(c->builder); @@ -408,34 +419,47 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) { DEBUG_LOG("Generating function %s.", decl->extname); assert(decl->backend_ref); + llvm_emit_body(c, + decl->backend_ref, + decl->unit->module->name->module, + decl->name, + decl->span.file_id, + decl->type->function.prototype, + decl->func_decl.attr_naked ? NULL : &decl->func_decl.signature, + astptr(decl->func_decl.body)); +} + +void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_name, const char *function_name, + FileId file_id, FunctionPrototype *prototype, Signature *signature, Ast *body) +{ bool emit_debug = llvm_use_debug(c); LLVMValueRef prev_function = c->function; LLVMBuilderRef prev_builder = c->builder; - c->opt_var = NULL; c->catch_block = NULL; - c->function = decl->backend_ref; + c->function = function; + if (!function_name) function_name = "anonymous function"; if (emit_debug) { - c->debug.function = LLVMGetSubprogram(c->function); + c->debug.function = LLVMGetSubprogram(function); if (c->debug.enable_stacktrace) { scratch_buffer_clear(); - scratch_buffer_append(decl->unit->module->name->module); + scratch_buffer_append(module_name); scratch_buffer_append("::"); - scratch_buffer_append(decl->name ? decl->name : "$anon"); + scratch_buffer_append(function_name); c->debug.func_name = llvm_emit_zstring(c, scratch_buffer_to_string()); - File *file = source_file_by_id(decl->span.file_id); + File *file = source_file_by_id(file_id); c->debug.file_name = llvm_emit_zstring(c, file->name); } } - c->cur_func_decl = decl; - + c->cur_func.name = function_name; + c->cur_func.prototype = prototype; LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry"); c->current_block = entry; c->current_block_is_target = true; @@ -446,7 +470,6 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) LLVMValueRef alloca_point = LLVMBuildAlloca(c->builder, LLVMInt32TypeInContext(c->context), "alloca_point"); c->alloca_point = alloca_point; - FunctionPrototype *prototype = decl->type->function.prototype; unsigned arg = 0; if (emit_debug) @@ -486,7 +509,7 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) c->failable_out = NULL; c->return_out = NULL; - if (prototype->ret_abi_info->kind == ABI_ARG_INDIRECT) + if (prototype && prototype->ret_abi_info->kind == ABI_ARG_INDIRECT) { if (prototype->is_failable) { @@ -497,26 +520,24 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) c->return_out = LLVMGetParam(c->function, arg++); } } - if (prototype->ret_by_ref_abi_info) + if (prototype && prototype->ret_by_ref_abi_info) { assert(!c->return_out); c->return_out = LLVMGetParam(c->function, arg++); } - if (!decl->func_decl.attr_naked) + if (signature) { // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code - VECEACH(decl->func_decl.signature.params, i) - { - llvm_emit_parameter(c, decl->func_decl.signature.params[i], prototype->abi_args[i], &arg, i); - } + FOREACH_BEGIN_IDX(i, Decl *param, signature->params) + llvm_emit_parameter(c, param, prototype->abi_args[i], &arg, i); + FOREACH_END(); } LLVMSetCurrentDebugLocation2(c->builder, NULL); - assert(decl->func_decl.body); - AstId current = astptr(decl->func_decl.body)->compound_stmt.first_stmt; + AstId current = body->compound_stmt.first_stmt; while (current) { llvm_emit_stmt(c, ast_next(¤t)); @@ -606,6 +627,34 @@ static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABI } } + +void llvm_emit_xxlizer(GenContext *c, Decl *decl) +{ + Ast *body = astptrzero(decl->xxlizer.init); + if (!body) + { + // Skip if it doesn't have a body. + return; + } + LLVMTypeRef initializer_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); + bool is_finalizer = decl->decl_kind == DECL_FINALIZE; + LLVMValueRef **array_ref = is_finalizer ? &c->destructors : &c->constructors; + scratch_buffer_clear(); + scratch_buffer_printf(is_finalizer ? ".static_finalize.%u" : ".static_initialize.%u", vec_size(*array_ref)); + LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type); + llvm_emit_body(c, + function, + decl->unit->module->name->module, + is_finalizer ? "[static finalizer]" : "[static initializer]", + decl->span.file_id, + NULL, + NULL, + body); + unsigned priority = decl->xxlizer.priority; + LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) }; + vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false)); +} + void llvm_emit_function_decl(GenContext *c, Decl *decl) { assert(decl->decl_kind == DECL_FUNC); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 7f7f43bc0..4f2733dac 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -72,6 +72,8 @@ typedef struct LLVMBuilderRef builder; LLVMBasicBlockRef current_block; LLVMBasicBlockRef catch_block; + LLVMValueRef *constructors; + LLVMValueRef *destructors; const char *ir_filename; const char *object_filename; const char *asm_filename; @@ -83,8 +85,12 @@ typedef struct LLVMTypeRef fault_type; LLVMTypeRef size_type; Decl *panicfn; - Decl *cur_code_decl; - Decl *cur_func_decl; + struct + { + const char *name; + FunctionPrototype *prototype; + Type *rtype; + } cur_func; TypeInfo *current_return_type; int block_global_unique_count; int ast_alloca_addr_space; @@ -267,6 +273,7 @@ LLVMMetadataRef llvm_get_debug_type(GenContext *c, Type *type); LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type); LLVMTypeRef llvm_get_pointee_type(GenContext *c, Type *any_type); void llvm_emit_function_decl(GenContext *c, Decl *decl); +void llvm_emit_xxlizer(GenContext *c, Decl *decl); INLINE LLVMTypeRef llvm_get_ptr_type(GenContext *c, Type *type); // -- Attributes --- diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 07c4164ee..beef96d39 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -181,7 +181,7 @@ static inline void llvm_emit_return(GenContext *c, Ast *ast) LLVMBasicBlockRef error_return_block = NULL; LLVMValueRef error_out = NULL; - if (type_is_optional(c->cur_func_decl->type->function.prototype->rtype)) + if (c->cur_func.prototype && type_is_optional(c->cur_func.prototype->rtype)) { error_return_block = llvm_basic_block_new(c, "err_retblock"); error_out = llvm_emit_alloca_aligned(c, type_anyerr, "reterr"); @@ -467,7 +467,7 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) SourceSpan loc = ast->span; File *file = source_file_by_id(loc.file_id); - llvm_emit_panic(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.name, loc.row ? loc.row : 1); LLVMBuildUnreachable(c->builder); LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block"); c->current_block = NULL; @@ -1001,7 +1001,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_panic(c, error, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1); + llvm_emit_panic(c, error, file->name, c->cur_func.name, loc.row ? loc.row : 1); llvm_emit_br(c, on_ok); llvm_emit_block(c, on_ok); } @@ -1298,7 +1298,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_panic(c, panic_name, file->name, c->cur_func_decl->name, loc.row); + llvm_emit_panic(c, panic_name, file->name, c->cur_func.name, loc.row); llvm_emit_br(c, ok_block); llvm_emit_block(c, ok_block); } @@ -1312,7 +1312,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_panic(c, panic_name, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1); + llvm_emit_panic(c, panic_name, file->name, c->cur_func.name, loc.row ? loc.row : 1); llvm_emit_br(c, ok_block); llvm_emit_block(c, ok_block); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 8aa80b9cb..fdb5881c5 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -5,6 +5,7 @@ static Decl *parse_const_declaration(ParseContext *c, Visibility visibility); static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility, AstId docs, bool is_interface); static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl); +static inline Decl *parse_static_top_level(ParseContext *c); #define DECL_VAR_NEW(type__, var__, visible__) decl_new_var(symstr(c), c->span, type__, var__, visible__); @@ -53,6 +54,7 @@ void recover_top_level(ParseContext *c) case TOKEN_STRUCT: case TOKEN_UNION: case TOKEN_BITSTRUCT: + case TOKEN_STATIC: case TYPELIKE_TOKENS: // Only recover if this is in the first col. if (c->span.col == 1) return; @@ -2173,6 +2175,37 @@ static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility return func; } +static inline Decl *parse_static_top_level(ParseContext *c) +{ + advance_and_verify(c, TOKEN_STATIC); + Decl *init = decl_calloc(); + if (!tok_is(c, TOKEN_IDENT)) + { + if (token_is_any_type(c->tok)) + { + SEMA_ERROR_HERE("'static' can only used with local variables, to hide global variables and functions, use 'private'."); + return poisoned_decl; + } + SEMA_ERROR_HERE("Expected 'static initialize' or 'static finalize'."); + return poisoned_decl; + } + init->decl_kind = DECL_INITIALIZE; + if (c->data.string == kw_finalize) + { + init->decl_kind = DECL_FINALIZE; + } + else if (c->data.string != kw_initialize) + { + SEMA_ERROR_HERE("Expected 'static initialize' or 'static finalize'."); + return poisoned_decl; + } + advance(c); + Attr *attr = NULL; + if (!parse_attributes(c, &init->attributes)) return poisoned_decl; + ASSIGN_ASTID_OR_RET(init->xxlizer.init, parse_compound_stmt(c), poisoned_decl); + RANGE_EXTEND_PREV(init); + return init; +} static inline bool check_no_visibility_before(ParseContext *c, Visibility visibility) { @@ -2522,6 +2555,17 @@ Decl *parse_top_level_statement(ParseContext *c) ASSIGN_DECL_OR_RET(decl, parse_func_definition(c, visibility, docs, false), poisoned_decl); break; } + case TOKEN_STATIC: + { + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; + ASSIGN_DECL_OR_RET(decl, parse_static_top_level(c), poisoned_decl); + if (docs) + { + SEMA_ERROR(astptr(docs), "Unexpected doc comment before 'static', did you mean to use a regular comment?"); + return poisoned_decl; + } + break; + } case TOKEN_CT_ASSERT: if (!check_no_visibility_before(c, visibility)) return poisoned_decl; { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 2b100dbde..f46373b83 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1419,6 +1419,12 @@ static const char *attribute_domain_to_string(AttributeDomain domain) return "typedef"; case ATTR_CALL: return "call"; + case ATTR_INITIALIZER: + return "static initializer"; + case ATTR_FINALIZER: + return "static finalizer"; + case ATTR_XXLIZER: + UNREACHABLE } UNREACHABLE } @@ -1429,7 +1435,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, assert(type >= 0 && type < NUMBER_OF_ATTRIBUTES); static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = { [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, - [ATTRIBUTE_EXTNAME] = (AttributeDomain)~(ATTR_CALL | ATTR_BITSTRUCT | ATTR_MACRO), + [ATTRIBUTE_EXTNAME] = (AttributeDomain)~(ATTR_CALL | ATTR_BITSTRUCT | ATTR_MACRO | ATTR_XXLIZER), [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION, [ATTRIBUTE_NORETURN] = ATTR_FUNC | ATTR_MACRO, @@ -1441,8 +1447,8 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_MAYDISCARD] = ATTR_FUNC | ATTR_MACRO, [ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, - [ATTRIBUTE_USED] = (AttributeDomain)~ATTR_CALL, - [ATTRIBUTE_UNUSED] = (AttributeDomain)~ATTR_CALL, + [ATTRIBUTE_USED] = (AttributeDomain)~(ATTR_CALL | ATTR_XXLIZER ), + [ATTRIBUTE_UNUSED] = (AttributeDomain)~(ATTR_CALL | ATTR_XXLIZER), [ATTRIBUTE_NAKED] = ATTR_FUNC, [ATTRIBUTE_CDECL] = ATTR_FUNC, [ATTRIBUTE_STDCALL] = ATTR_FUNC, @@ -1455,6 +1461,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_REFLECT] = ATTR_ENUM, [ATTRIBUTE_OBFUSCATE] = ATTR_ENUM, [ATTRIBUTE_PURE] = ATTR_CALL, + [ATTRIBUTE_PRIORITY] = ATTR_XXLIZER, }; if ((attribute_domain[type] & domain) != domain) @@ -1551,7 +1558,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, return false; } if (!sema_analyse_expr(context, expr)) return false; - if (expr->expr_kind != EXPR_CONST || !type_is_integer(expr->type->canonical)) + if (!expr_is_const_int(expr)) { SEMA_ERROR(expr, "Expected a constant integer value as argument."); return false; @@ -1589,7 +1596,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, return false; } if (!sema_analyse_expr(context, expr)) return false; - if (expr->expr_kind != EXPR_CONST || expr->const_expr.const_kind != CONST_STRING) + if (!expr_is_const_string(expr)) { SEMA_ERROR(expr, "Expected a constant string value as argument."); return false; @@ -1660,6 +1667,19 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, case ATTRIBUTE_USED: decl->is_must_use = true; break; + case ATTRIBUTE_PRIORITY: + if (!expr || !expr_is_const_int(expr)) goto ERROR_PRIORITY; + { + Int i = expr->const_expr.ixx; + if (!int_fits(i, TYPE_I64)) goto ERROR_PRIORITY; + int64_t priority = int_to_i64(i); + if (priority < 1 || priority > MAX_PRIORITY) goto ERROR_PRIORITY; + decl->xxlizer.priority = priority; + return true; + } + ERROR_PRIORITY: + SEMA_ERROR(attr, "Expected an argument to '@priority' between 1 and %d.", MAX_PRIORITY); + return decl_poison(decl); case ATTRIBUTE_PURE: // Only used for calls. UNREACHABLE @@ -2001,6 +2021,51 @@ static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, boo return true; } +static inline bool sema_analyse_xxlizer(SemaContext *context, Decl *decl) +{ + if (!sema_analyse_attributes(context, decl, decl->attributes, decl->decl_kind == DECL_INITIALIZE ? ATTR_INITIALIZER : ATTR_FINALIZER)) return decl_poison(decl); + if (decl->xxlizer.priority == 0) decl->xxlizer.priority = MAX_PRIORITY; + context->current_function = NULL; + context->current_function_pure = false; + context->rtype = type_void; + context->active_scope = (DynamicScope) { + .scope_id = 0, + .depth = 0, + .label_start = 0, + .current_local = 0 + }; + + // Clear returns + vec_resize(context->returns, 0); + context->scope_id = 0; + context->continue_target = NULL; + context->next_target = 0; + context->next_switch = 0; + context->break_target = 0; + context->ensures = false; + Ast *body = astptr(decl->xxlizer.init); + + // Insert an implicit return + AstId *next_id = &body->compound_stmt.first_stmt; + if (!*next_id) + { + decl->xxlizer.init = 0; + } + SourceSpan span = body->span; + if (*next_id) + { + Ast *last = ast_last(astptr(*next_id)); + // Cleanup later + if (last->ast_kind == AST_RETURN_STMT) goto SKIP_NEW_RETURN; + span = last->span; + next_id = &last->next; + } + Ast *ret = new_ast(AST_RETURN_STMT, span); + ast_append(&next_id, ret); +SKIP_NEW_RETURN: + return sema_analyse_statement(context, body); +} + static inline bool sema_analyse_func(SemaContext *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); @@ -2652,6 +2717,10 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_DEFINE: if (!sema_analyse_define(context, decl)) goto FAILED; break; + case DECL_INITIALIZE: + case DECL_FINALIZE: + if (!sema_analyse_xxlizer(context, decl)) goto FAILED; + break; case DECL_POISONED: case DECL_IMPORT: case DECL_ENUM_CONSTANT: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f582855bd..c10716c80 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -477,6 +477,8 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_TYPEDEF: case DECL_DECLARRAY: case DECL_BODYPARAM: + case DECL_INITIALIZE: + case DECL_FINALIZE: UNREACHABLE case DECL_POISONED: return expr_poison(expr); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index a0d356388..a02b92e81 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -404,6 +404,10 @@ void sema_analysis_pass_decls(Module *module) { sema_analyse_decl(&context, unit->generic_defines[i]); } + VECEACH(unit->xxlizers, i) + { + sema_analyse_decl(&context, unit->xxlizers[i]); + } sema_context_destroy(&context); } DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 6950be227..ac187760b 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -194,6 +194,8 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_ATTRIBUTE: SEMA_ERROR(type_info, "This is not a type."); return type_info_poison(type_info); + case DECL_INITIALIZE: + case DECL_FINALIZE: case DECL_CT_ELSE: case DECL_CT_IF: case DECL_CT_ELIF: diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index cdc659c5b..cf595b0e4 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -167,6 +167,8 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) case DECL_LABEL: case DECL_CT_ASSERT: case DECL_DECLARRAY: + case DECL_INITIALIZE: + case DECL_FINALIZE: continue; case DECL_ATTRIBUTE: break; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index fcc060b22..985358c83 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -57,16 +57,18 @@ const char *kw_check_assign; const char *kw_deprecated; const char *kw_distinct; const char *kw_elements; +const char *kw_finalize; const char *kw_in; const char *kw_incr; const char *kw_inf; +const char *kw_initialize; const char *kw_inline; const char *kw_inner; const char *kw_inout; const char *kw_kind; const char *kw_len; -const char *kw_mainstub; const char *kw_main; +const char *kw_mainstub; const char *kw_max; const char *kw_min; const char *kw_nameof; @@ -82,8 +84,8 @@ const char *kw_sizeof; const char *kw_std; const char *kw_std__core; const char *kw_std__core__types; -const char *kw_typekind; const char *kw_type; +const char *kw_typekind; const char *kw_values; void symtab_destroy() @@ -142,7 +144,9 @@ void symtab_init(uint32_t capacity) kw_deprecated = KW_DEF("deprecated"); kw_distinct = KW_DEF("distinct"); kw_elements = KW_DEF("elements"); + kw_finalize = KW_DEF("finalize"); kw_in = KW_DEF("in"); + kw_initialize = KW_DEF("initialize"); kw_incr = KW_DEF("incr"); kw_inf = KW_DEF("inf"); kw_inline = KW_DEF("inline"); @@ -262,6 +266,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("@operator"); attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("@overlap"); attribute_list[ATTRIBUTE_PACKED] = KW_DEF("@packed"); + attribute_list[ATTRIBUTE_PRIORITY] = KW_DEF("@priority"); attribute_list[ATTRIBUTE_PURE] = kw_at_pure; attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect"); attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("@regcall"); diff --git a/src/compiler/target.c b/src/compiler/target.c index f6e979cf7..c6b9e38be 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -3,6 +3,8 @@ #include #include "compiler_internal.h" +extern void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array); + static unsigned arch_pointer_bit_width(OsType os, ArchType arch); static ArchType arch_from_llvm_string(StringSlice string); static EnvironmentType environment_type_from_llvm_string(StringSlice string); @@ -1216,6 +1218,7 @@ static AlignSize os_target_pref_alignment_of_float(OsType os, ArchType arch, uin } + void *llvm_target_machine_create(void) { char *err = NULL; @@ -1271,6 +1274,7 @@ void *llvm_target_machine_create(void) platform_target.cpu ? platform_target.cpu : "", features, (LLVMCodeGenOptLevel)platform_target.llvm_opt_level, reloc_mode, LLVMCodeModelDefault); + LLVMSetTargetMachineUseInitArray(result, true); if (!result) error_exit("Failed to create target machine."); LLVMSetTargetMachineAsmVerbosity(result, 1); return result; diff --git a/src/version.h b/src/version.h index 85821cd81..8900f77b5 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.59" \ No newline at end of file +#define COMPILER_VERSION "0.3.60" \ No newline at end of file diff --git a/test/test_suite/from_docs/examples_forswitch.c3t b/test/test_suite/from_docs/examples_forswitch.c3t index d1b175361..b65436fac 100644 --- a/test/test_suite/from_docs/examples_forswitch.c3t +++ b/test/test_suite/from_docs/examples_forswitch.c3t @@ -109,7 +109,7 @@ loop.body: ; preds = %loop.cond br label %loop.cond loop.exit: ; preds = %loop.cond - call void @std_core_builtin_panic(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.zstr, i64 0, i64 0), i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.zstr.1, i64 0, i64 0), i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.zstr.2, i64 0, i64 0), i32 14) + call void @std_core_builtin_panic(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.zstr, i64 0, i64 0), i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.zstr.1, i64 0, i64 0), i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.zstr.2, i64 0, i64 0), i32 14) unreachable unreachable_block: ; No predecessors! diff --git a/test/test_suite/globals/static_global.c3 b/test/test_suite/globals/static_global.c3 index 227d5fd13..cdeaf9872 100644 --- a/test/test_suite/globals/static_global.c3 +++ b/test/test_suite/globals/static_global.c3 @@ -1 +1 @@ -static int ifej; // #error: Expected a top level declaration here \ No newline at end of file +static int ifej; // #error: 'static' can only used with local variables \ No newline at end of file diff --git a/test/test_suite/initialize/initialize_bad_prio.c3 b/test/test_suite/initialize/initialize_bad_prio.c3 new file mode 100644 index 000000000..97674d114 --- /dev/null +++ b/test/test_suite/initialize/initialize_bad_prio.c3 @@ -0,0 +1,16 @@ + +static initialize @priority("hello") // #error: Expected an argument to '@priority' +{ +} + +static initialize @priority(1, 2) // #error: Too many arguments for +{ +} + +static initialize @priority(0) // #error: Expected an argument to '@priority' +{ +} + +static initialize @priority(65536) // #error: Expected an argument to '@priority' +{ +} \ No newline at end of file diff --git a/test/test_suite/initialize/initialize_finalize.c3t b/test/test_suite/initialize/initialize_finalize.c3t new file mode 100644 index 000000000..708f97ac7 --- /dev/null +++ b/test/test_suite/initialize/initialize_finalize.c3t @@ -0,0 +1,62 @@ +// #target: linux-x64 +module test; +import std::io; + +fn void main() +{ + io::println("Hello, world!"); +} +extern fn void puts(char*); + +static initialize @priority(300) +{ + puts("Hello startup2"); +} +static initialize +{ + puts("Let's start main..."); +} +static initialize @priority(200) +{ + puts("Hello startup"); +} + +static initialize +{} + +static finalize +{ + puts("Bye bye"); +} + +/* #expect: test.ll + +@llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 300, void ()* @.static_initialize.0, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @.static_initialize.1, i8* null }, { i32, void ()*, i8* } { i32 200, void ()* @.static_initialize.2, i8* null }] +@llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @.static_finalize.0, i8* null }] + +define void @.static_initialize.0() { +entry: + call void @puts(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i32 0, i32 0)) + ret void +} + +; Function Attrs: nounwind +declare void @puts(i8*) #0 + +define void @.static_initialize.1() { +entry: + call void @puts(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.1, i32 0, i32 0)) + ret void +} + +define void @.static_initialize.2() { +entry: + call void @puts(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0)) + ret void +} + +define void @.static_finalize.0() { +entry: + call void @puts(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i32 0, i32 0)) + ret void +} diff --git a/test/test_suite/initialize/initialize_jump.c3 b/test/test_suite/initialize/initialize_jump.c3 new file mode 100644 index 000000000..51b46420a --- /dev/null +++ b/test/test_suite/initialize/initialize_jump.c3 @@ -0,0 +1,9 @@ +static initialize +{ + return; // This is fine +} + +static initialize +{ + return 123; // #error: You cannot cast 'int' into 'void' even with an explicit cast, so this looks like an error. +} \ No newline at end of file diff --git a/test/test_suite/initialize/initialize_parse_error.c3 b/test/test_suite/initialize/initialize_parse_error.c3 new file mode 100644 index 000000000..b1b361c91 --- /dev/null +++ b/test/test_suite/initialize/initialize_parse_error.c3 @@ -0,0 +1 @@ +static foo {} ; // #error: Expected 'static initialize' diff --git a/test/test_suite/initialize/initialize_prio.c3 b/test/test_suite/initialize/initialize_prio.c3 new file mode 100644 index 000000000..87f929f01 --- /dev/null +++ b/test/test_suite/initialize/initialize_prio.c3 @@ -0,0 +1,3 @@ +static initialize @priority() // #error: An expression was expected. +{ +} diff --git a/test/test_suite2/globals/static_global.c3 b/test/test_suite2/globals/static_global.c3 index 227d5fd13..b8fdf0397 100644 --- a/test/test_suite2/globals/static_global.c3 +++ b/test/test_suite2/globals/static_global.c3 @@ -1 +1 @@ -static int ifej; // #error: Expected a top level declaration here \ No newline at end of file +static int ifej; // #error: 'static' can only used with local variables, to hide global variables \ No newline at end of file diff --git a/test/test_suite2/initialize/initialize_bad_prio.c3 b/test/test_suite2/initialize/initialize_bad_prio.c3 new file mode 100644 index 000000000..97674d114 --- /dev/null +++ b/test/test_suite2/initialize/initialize_bad_prio.c3 @@ -0,0 +1,16 @@ + +static initialize @priority("hello") // #error: Expected an argument to '@priority' +{ +} + +static initialize @priority(1, 2) // #error: Too many arguments for +{ +} + +static initialize @priority(0) // #error: Expected an argument to '@priority' +{ +} + +static initialize @priority(65536) // #error: Expected an argument to '@priority' +{ +} \ No newline at end of file diff --git a/test/test_suite2/initialize/initialize_finalize.c3t b/test/test_suite2/initialize/initialize_finalize.c3t new file mode 100644 index 000000000..9b1fbbb89 --- /dev/null +++ b/test/test_suite2/initialize/initialize_finalize.c3t @@ -0,0 +1,62 @@ +// #target: linux-x64 +module test; +import std::io; + +fn void main() +{ + io::println("Hello, world!"); +} +extern fn void puts(char*); + +static initialize @priority(300) +{ + puts("Hello startup2"); +} +static initialize +{ + puts("Let's start main..."); +} +static initialize @priority(200) +{ + puts("Hello startup"); +} + +static initialize +{} + +static finalize +{ + puts("Bye bye"); +} + +/* #expect: test.ll + +@llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 300, ptr @.static_initialize.0, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @.static_initialize.1, ptr null }, { i32, ptr, ptr } { i32 200, ptr @.static_initialize.2, ptr null }] +@llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @.static_finalize.0, ptr null }] + +define void @.static_initialize.0() { +entry: + call void @puts(ptr @.str) + ret void +} + +; Function Attrs: nounwind +declare void @puts(ptr) #0 + +define void @.static_initialize.1() { +entry: + call void @puts(ptr @.str.1) + ret void +} + +define void @.static_initialize.2() { +entry: + call void @puts(ptr @.str.2) + ret void +} + +define void @.static_finalize.0() { +entry: + call void @puts(ptr @.str.3) + ret void +} \ No newline at end of file diff --git a/test/test_suite2/initialize/initialize_jump.c3 b/test/test_suite2/initialize/initialize_jump.c3 new file mode 100644 index 000000000..51b46420a --- /dev/null +++ b/test/test_suite2/initialize/initialize_jump.c3 @@ -0,0 +1,9 @@ +static initialize +{ + return; // This is fine +} + +static initialize +{ + return 123; // #error: You cannot cast 'int' into 'void' even with an explicit cast, so this looks like an error. +} \ No newline at end of file diff --git a/test/test_suite2/initialize/initialize_parse_error.c3 b/test/test_suite2/initialize/initialize_parse_error.c3 new file mode 100644 index 000000000..b1b361c91 --- /dev/null +++ b/test/test_suite2/initialize/initialize_parse_error.c3 @@ -0,0 +1 @@ +static foo {} ; // #error: Expected 'static initialize' diff --git a/test/test_suite2/initialize/initialize_prio.c3 b/test/test_suite2/initialize/initialize_prio.c3 new file mode 100644 index 000000000..87f929f01 --- /dev/null +++ b/test/test_suite2/initialize/initialize_prio.c3 @@ -0,0 +1,3 @@ +static initialize @priority() // #error: An expression was expected. +{ +} diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index b36fce818..696a46393 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -5,6 +5,8 @@ #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Object/SymbolicFile.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/Target/TargetMachine.h" #if LLVM_VERSION_MAJOR > 13 #define LINK_SIG \ @@ -167,6 +169,12 @@ extern "C" { int llvm_version_major = LLVM_VERSION_MAJOR; + void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array) + { + auto machine = reinterpret_cast(ref); + machine->Options.UseInitArray = use_init_array; + } + LLVMValueRef LLVMConstBswap(LLVMValueRef ConstantVal) { llvm::Constant *Val = llvm::unwrap(ConstantVal);