Some cleanup of asm and assert

This commit is contained in:
Christoffer Lerno
2024-09-03 13:53:15 +02:00
parent df91ee3d2a
commit bbc199cda3
5 changed files with 82 additions and 58 deletions

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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
}
/**