From bbc199cda340522c0c54f632a383870bf113ef5a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 3 Sep 2024 13:53:15 +0200 Subject: [PATCH] Some cleanup of asm and assert --- src/compiler/asm_target.c | 23 +++++++++-- src/compiler/compiler_internal.h | 3 +- src/compiler/sema_asm.c | 41 +++++++++--------- src/compiler/sema_builtins.c | 2 +- src/compiler/sema_stmts.c | 71 ++++++++++++++++++-------------- 5 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/compiler/asm_target.c b/src/compiler/asm_target.c index d104fb136..397312d26 100644 --- a/src/compiler/asm_target.c +++ b/src/compiler/asm_target.c @@ -219,13 +219,13 @@ INLINE void reg_register_list(PlatformTarget *target, const char **names, unsign for (unsigned i = 0; i < count; i++) reg_register(target, names[i], param, bitsize, i + first_clobber); } -AsmInstruction *asm_instr_by_name(PlatformTarget *target, const char *name) +AsmInstruction *asm_instr_by_name(const char *name) { uint32_t hash = ASM_PTR_HASH(name); uint32_t slot = hash & ASM_INSTRUCTION_MASK; while (1) { - AsmInstruction *inst = &target->instructions[slot]; + AsmInstruction *inst = &compiler.platform.instructions[slot]; if (inst->name == name) return inst; if (inst->name == NULL) return NULL; slot = (slot + 1) & ASM_INSTRUCTION_MASK; @@ -608,13 +608,28 @@ static void init_asm_x86(PlatformTarget* target) reg_register_list(target, x86_zmm_regs, 16, ASM_REF_FVEC, ARG_BITS_512, X86_XMM0); } } + +bool asm_is_supported(ArchType arch) +{ + switch (arch) + { + case ARCH_TYPE_X86: + case ARCH_TYPE_X86_64: + case ARCH_TYPE_RISCV32: + case ARCH_TYPE_RISCV64: + case ARCH_TYPE_AARCH64: + return true; + default: + return false; + } +} + void init_asm(PlatformTarget *target) { if (target->asm_initialized) return; target->asm_initialized = true; switch (target->arch) { - case ARCH_UNSUPPORTED: case ARCH_TYPE_X86_64: case ARCH_TYPE_X86: init_asm_x86(target); @@ -647,6 +662,8 @@ void init_asm(PlatformTarget *target) case ARCH_TYPE_RISCV64: init_asm_riscv(target); return; + case ARCH_UNSUPPORTED: + error_exit("Arch is unsupported and does not support inline asm."); } UNREACHABLE } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c836cb35d..4b9d01754 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2023,11 +2023,12 @@ TypeInfo *copy_type_info_single(TypeInfo *type_info); void init_asm(PlatformTarget *target); AsmRegister *asm_reg_by_name(PlatformTarget *target, const char *name); -AsmInstruction *asm_instr_by_name(PlatformTarget *target, const char *name); +AsmInstruction *asm_instr_by_name(const char *name); INLINE const char *asm_clobber_by_index(unsigned index); INLINE AsmRegister *asm_reg_by_index(unsigned index); AsmRegister *asm_reg_by_index(unsigned index); +bool asm_is_supported(ArchType arch); bool cast_implicit_silent(SemaContext *context, Expr *expr, Type *to_type, bool is_binary_conversion); diff --git a/src/compiler/sema_asm.c b/src/compiler/sema_asm.c index 52e18ae44..d9ec5227d 100644 --- a/src/compiler/sema_asm.c +++ b/src/compiler/sema_asm.c @@ -1,12 +1,17 @@ +// Copyright (c) 2022-2024 Christoffer Lerno and contributors. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + #include "sema_internal.h" #include "compiler/asm/x86.h" - +// Add a single clobber to a block. static inline void sema_add_clobber(AsmInlineBlock *block, unsigned index) { clobbers_add(&block->clobbers, index); } +// Add a full clobber mask to the clobbers. static inline void sema_add_clobbers(AsmInlineBlock *block, Clobbers *clobbers) { for (unsigned i = 0; i < CLOBBER_FLAG_ELEMENTS; i++) @@ -543,39 +548,33 @@ static inline bool sema_check_asm_arg(SemaContext *context, AsmInlineBlock *bloc } UNREACHABLE } + bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt) { - if (compiler.platform.arch != ARCH_TYPE_X86_64 && - compiler.platform.arch != ARCH_TYPE_AARCH64 && - compiler.platform.arch != ARCH_TYPE_RISCV32 && - compiler.platform.arch != ARCH_TYPE_RISCV64) - { - SEMA_ERROR(asm_stmt, "Unsupported architecture for asm."); - return false; - } - init_asm(&compiler.platform); - AsmInstruction *instr = asm_instr_by_name(&compiler.platform, asm_stmt->asm_stmt.instruction); - if (!instr) - { - SEMA_ERROR(asm_stmt, "Unknown instruction"); - return false; - } + assert(compiler.platform.asm_initialized); + + AsmInstruction *instr = asm_instr_by_name(asm_stmt->asm_stmt.instruction); + if (!instr) RETURN_SEMA_ERROR(asm_stmt, "Unknown instruction"); + + // Check arguments Expr **args = asm_stmt->asm_stmt.args; unsigned expected_params = instr->param_count; unsigned arg_count = vec_size(args); if (expected_params != arg_count) { - SEMA_ERROR(asm_stmt, "Too %s arguments to instruction '%s', expected %d.", - expected_params > arg_count ? "few" : "many", - instr->name, expected_params); - return false; + RETURN_SEMA_ERROR(asm_stmt, "Too %s arguments to instruction '%s', expected %d.", + expected_params > arg_count ? "few" : "many", + instr->name, expected_params); } + + // Sema check each argument. for (unsigned i = arg_count; i > 0; i--) { if (!sema_check_asm_arg(context, block, instr, instr->param[i - 1], args[i - 1])) return false; } + + // Add clobbers sema_add_clobbers(block, &instr->mask); - //const char *variant = asm_stmt->asm_stmt.variant; return true; } diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 9f67a186a..cb64544a4 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Christoffer Lerno. All rights reserved. +// Copyright (c) 2022-2024 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. #include "sema_internal.h" diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 97e3ae1c8..7171fec2d 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -54,6 +54,13 @@ static bool sema_analyse_optional_returns(SemaContext *context, Ast *directive); static inline bool sema_analyse_asm_stmt(SemaContext *context, Ast *stmt) { if (stmt->asm_block_stmt.is_string) return sema_analyse_asm_string_stmt(context, stmt); + // Check for support + if (!asm_is_supported(compiler.platform.arch)) + { + RETURN_SEMA_ERROR(stmt, "This architecture does not support inline asm."); + } + // Init as needed. + init_asm(&compiler.platform); AsmInlineBlock *block = stmt->asm_block_stmt.block; AstId ast_id = block->asm_stmt; scratch_buffer_clear(); @@ -67,11 +74,9 @@ static inline bool sema_analyse_asm_stmt(SemaContext *context, Ast *stmt) } /** - * assert(foo), assert(foo, message), assert(try foo), assert(try foo, message) + * assert(foo), assert(foo, message, ...) * - * - Using the try construct we implicitly unpack the variable if present. - * - assert(false) will implicitly make the rest of the code marked as unreachable. - * - assert might also be used for 'ensure' and in this case violating it is a compile time error. + * - assert(false) is a compile time error. */ static inline bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement) { @@ -86,7 +91,8 @@ static inline bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement FOREACH(Expr *, e, statement->assert_stmt.args) { if (!sema_analyse_expr(context, e)) return false; - if (IS_OPTIONAL(e)) RETURN_SEMA_ERROR(e, "Optionals cannot be used as assert arguments, use '?" "?', '!' or '!!' to fix this."); + if (IS_OPTIONAL(e)) RETURN_SEMA_ERROR(e, "Optionals cannot be used as assert arguments, use '?" + "?', '!' or '!!' to fix this."); if (type_is_void(e->type)) RETURN_SEMA_ERROR(e, "This expression is of type 'void', did you make a mistake?"); } } @@ -102,41 +108,42 @@ static inline bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement if (!sema_analyse_cond_expr(context, expr, &result)) return false; // If it's constant, we process it differently. - if (result != COND_MISSING) + switch (result) { - // It's true, then replace the statement with a nop. - if (expr->const_expr.b) - { + case COND_TRUE: + // It's true, then replace the statement with a nop. statement->ast_kind = AST_NOP_STMT; return true; - } - // Was this 'assert(false)'? - if (result_no_resolve == COND_FALSE) - { - assert(result == COND_FALSE); - // If this is a test, then assert(false) is permitted. - if (context->call_env.current_function && context->call_env.current_function->func_decl.attr_test) + case COND_FALSE: + // Was this 'assert(false)'? + if (result_no_resolve == COND_FALSE) { - context->active_scope.jump_end = true; - return true; + // If this is a test, then assert(false) is permitted. + if (context->call_env.current_function && context->call_env.current_function->func_decl.attr_test) + { + context->active_scope.jump_end = true; + return true; + } + // Otherwise, require unreachable. + RETURN_SEMA_ERROR(expr, "Use 'unreachable' instead of 'assert(false)'."); } - // Otherwise, require unreachable. - RETURN_SEMA_ERROR(expr, "Use 'unreachable' instead of 'assert(false)'."); - } - - // If it's ensure (and an error) we print an error. - if (!context->active_scope.jump_end && !context->active_scope.is_dead) - { - if (message_expr && sema_cast_const(message_expr) && vec_size(statement->assert_stmt.args)) + // Otherwise we print an error. + if (!context->active_scope.jump_end && !context->active_scope.is_dead) { - RETURN_SEMA_ERROR(expr, "%.*s", EXPAND_EXPR_STRING(message_expr)); + if (message_expr && sema_cast_const(message_expr) && vec_size(statement->assert_stmt.args)) + { + RETURN_SEMA_ERROR(expr, "%.*s", EXPAND_EXPR_STRING(message_expr)); + } + if (statement->assert_stmt.is_ensure) RETURN_SEMA_ERROR(expr, "Contract violated."); + RETURN_SEMA_ERROR(expr, "This expression will always be 'false'."); } - if (statement->assert_stmt.is_ensure) RETURN_SEMA_ERROR(expr, "Contract violated."); - RETURN_SEMA_ERROR(expr, "This expression will always be 'false'."); - } - // Otherwise, continue, this is fine as it can't be reached. + // Otherwise, continue, this is fine as it can't be reached. + return true; + case COND_MISSING: + // If the assert isn't compile time resolvable, we keep the assert. + return true; } - return true; + UNREACHABLE } /**