mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Some cleanup of asm and assert
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user