diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b79d808b..02e224ded 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,6 +342,7 @@ add_executable(c3c src/utils/whereami.c src/utils/cpus.c src/utils/unzipper.c + src/compiler/c_codegen.c src/compiler/decltable.c src/compiler/mac_support.c src/compiler/windows_support.c diff --git a/src/build/build.h b/src/build/build.h index f8ac925fc..eba59c34f 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -17,8 +17,9 @@ typedef enum { - BACKEND_LLVM = 1, - BACKEND_TB = 2 + BACKEND_LLVM = 0, + BACKEND_TB = 1, + BACKEND_C = 2, } CompilerBackend; typedef enum diff --git a/src/build/build_internal.h b/src/build/build_internal.h index aa3bb4209..5d9227a7c 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -88,6 +88,12 @@ static const char *optlevels[4] = { [OPTIMIZATION_AGGRESSIVE] = "max", }; +static const char *backends[3] = { + [BACKEND_LLVM] = "llvm", + [BACKEND_TB] = "tb", + [BACKEND_C] = "c", +}; + static const char *backtrace_levels[2] = { [SHOW_BACKTRACE_OFF] = "off", [SHOW_BACKTRACE_ON] = "on", diff --git a/src/build/build_options.c b/src/build/build_options.c index 6078dd2fe..646b14807 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -654,6 +654,11 @@ static void parse_option(BuildOptions *options) print_version(); exit_compiler(COMPILER_SUCCESS_EXIT); } + if ((argopt = match_argopt("backend"))) + { + options->backend = (CompilerBackend)parse_multi_option(argopt, 3, backends); + return; + } if (match_longopt("run-once")) { options->run_once = true; diff --git a/src/compiler/c_codegen.c b/src/compiler/c_codegen.c new file mode 100644 index 000000000..0dc38d61f --- /dev/null +++ b/src/compiler/c_codegen.c @@ -0,0 +1,1079 @@ +#include "c_codegen_internal.h" + + +typedef struct OptionalCatch_ +{ + const char* fault; + int block; +} OptionalCatch; + + +#define PUSH_CATCH() OptionalCatch _old_catch = c->catch +#define PUSH_CATCH_VAR_BLOCK(fault_, block_) OptionalCatch _old_catch = c->catch; c->catch = (OptionalCatch) { fault_, block_ } +#define PUSH_CLEAR_CATCH() OptionalCatch _old_catch = c->catch; c->catch = (OptionalCatch) { NULL, 0 } +#define POP_CATCH() c->catch = _old_catch + +typedef int VariableId; + +typedef enum +{ + CV_VALUE, + CV_ADDRESS, + CV_OPTIONAL_ADDRESS, +} CValueType; + +typedef struct CValue +{ + CValueType kind; + VariableId optional; + VariableId var; + Type *type; +} CValue; + +typedef struct +{ + HTable gen_decl; + HTable gen_def; + FILE *file; + int x; + int typename; + int id_gen; + OptionalCatch catch; +} GenContext; + +#define PRINTF(x, ...) fprintf(c->file, x, ## __VA_ARGS__) /* NOLINT */ +#define PRINT(x, ...) fputs(x, c->file) /* NOLINT */ + +static void c_emit_stmt(GenContext *c, Ast *stmt); + +static const char *c_type_name(GenContext *c, Type *type) +{ + type = type_lowering(type); + switch (type->type_kind) + { + case TYPE_VOID: + return "void"; + case TYPE_BOOL: + return "bool"; + case TYPE_POINTER: + return "void*"; + case TYPE_I8: + return "int8_t"; + case TYPE_I16: + return "int16_t"; + case TYPE_I32: + return "int32_t"; + case TYPE_I64: + return "int64_t"; + case TYPE_I128: + return "__c3_int128"; + case TYPE_U8: + return "uint8_t"; + case TYPE_U16: + return "uint16_t"; + case TYPE_U32: + return "uint32_t"; + case TYPE_U64: + return "uint64_t"; + case TYPE_U128: + return "__c3_uint128"; + case TYPE_F16: + error_exit("float16 not supported in the C backend."); + case TYPE_BF16: + error_exit("bfloat16 not supported in the C backend."); + case TYPE_F32: + return "float"; + case TYPE_F64: + return "double"; + case TYPE_F128: + error_exit("float128 not supported in the C backend."); + case TYPE_ANYFAULT: + case TYPE_TYPEID: + case TYPE_INTERFACE: + case TYPE_DISTINCT: + case TYPE_FUNC_RAW: + case TYPE_TYPEDEF: + case TYPE_ENUM: + case TYPE_UNTYPED_LIST: + case TYPE_INFERRED_ARRAY: + case TYPE_INFERRED_VECTOR: + case TYPE_OPTIONAL: + case TYPE_TYPEINFO: + case TYPE_WILDCARD: + case TYPE_MEMBER: + case TYPE_POISONED: + UNREACHABLE + case TYPE_ANY: + return "__c3_any__"; + case TYPE_FUNC_PTR: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_BITSTRUCT: + case TYPE_FAULTTYPE: + case TYPE_SLICE: + case TYPE_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + case TYPE_VECTOR: + { + void *prev = htable_get(&c->gen_decl, type); + if (!prev) return "NOT_REGISTERED"; + return prev; + } + } + UNREACHABLE +} +static bool c_emit_type_decl(GenContext *c, Type *type) +{ + type = type_lowering(type); + if (type == type_u128 || type == type_i128) + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + PRINT("typedef struct { uint64_t hi; uint64_t lo; } "); + PRINT(type == type_u128 ? "__c3_uint128;\n" : "__c3_int128;\n"); + htable_set(&c->gen_decl, type, type == type_u128 ? "__c3_uint128" : "__c3_int128"); + return true; + } + switch (type->type_kind) + { + case TYPE_POISONED: + case TYPE_VOID: + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + case TYPE_POINTER: + return false; + case TYPE_DISTINCT: + case TYPE_FUNC_RAW: + case TYPE_TYPEDEF: + case TYPE_ENUM: + case TYPE_UNTYPED_LIST: + case TYPE_INFERRED_ARRAY: + case TYPE_INFERRED_VECTOR: + case TYPE_OPTIONAL: + case TYPE_TYPEINFO: + case TYPE_WILDCARD: + case TYPE_MEMBER: + case TYPE_ANYFAULT: + case TYPE_TYPEID: + case TYPE_INTERFACE: + UNREACHABLE + case TYPE_ANY: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + PRINTF("typedef struct { void* ptr; void* typeid; } __c3_any__;\n"); + htable_set(&c->gen_decl, type, "__c3_any__"); + return true; + } + case TYPE_FUNC_PTR: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + Type *base = type; + type = type->pointer; + FunctionPrototype *proto = type->function.prototype; + c_emit_type_decl(c, proto->rtype); + FOREACH (Type *, t, proto->param_types) + { + c_emit_type_decl(c, t); + } + int id = ++c->typename; + PRINTF("typedef %s(*__c3_fn%d)(", c_type_name(c, proto->rtype), id); + FOREACH_IDX(i, Type *, t, proto->param_types) + { + if (i != 0) PRINT(","); + PRINT(c_type_name(c, t)); + } + PRINT(");\n"); + scratch_buffer_clear(); + scratch_buffer_printf("__c3_fn%d", id); + htable_set(&c->gen_decl, base, scratch_buffer_copy()); + return true; + } + case TYPE_STRUCT: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + Decl *d = type->decl; + FOREACH(Decl *, m, d->strukt.members) + { + c_emit_type_decl(c, m->type); + } + PRINTF("typedef struct { "); + int id = ++c->typename; + FOREACH_IDX(i, Decl *, m, d->strukt.members) + { + PRINTF("%s m%d; ", c_type_name(c, m->type), i); + } + PRINTF("} __c3_struct_%d;\n", id); + scratch_buffer_clear(); + scratch_buffer_printf("__c3_struct_%d", id); + htable_set(&c->gen_decl, type, scratch_buffer_copy()); + return true; + } + case TYPE_UNION: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + Decl *d = type->decl; + FOREACH(Decl *, m, d->strukt.members) + { + c_emit_type_decl(c, m->type); + } + PRINTF("typedef union { "); + int id = ++c->typename; + FOREACH_IDX(i, Decl *, m, d->strukt.members) + { + PRINTF("%s m%d; ", c_type_name(c, m->type), i); + } + PRINTF("} __c3_union_%d;\n", id); + scratch_buffer_clear(); + scratch_buffer_printf("__c3_union_%d", id); + htable_set(&c->gen_decl, type, scratch_buffer_copy()); + return true; + } + case TYPE_BITSTRUCT: + case TYPE_FAULTTYPE: + TODO + break; + case TYPE_SLICE: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + c_emit_type_decl(c, type->array.base); + int id = ++c->typename; + PRINTF("typedef struct { %s* ptr; void* typeid; } __c3_slice%d;\n", c_type_name(c, type->array.base), id); + scratch_buffer_clear(); + scratch_buffer_printf(" __c3_slice%d", id); + htable_set(&c->gen_decl, type, scratch_buffer_copy()); + return true; + } + case TYPE_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + { + void *prev = htable_get(&c->gen_decl, type); + if (prev) return false; + c_emit_type_decl(c, type->array.base); + int id = ++c->typename; + PRINTF("typedef struct { %s ptr[%u]; } __c3_array%d;\n", c_type_name(c, type->array.base), type->array.len, id); + scratch_buffer_clear(); + scratch_buffer_printf(" __c3_array%d", id); + htable_set(&c->gen_decl, type, scratch_buffer_copy()); + return true; + } + case TYPE_VECTOR: + error_exit("Vectors are not supported in the C backend yet."); + } + UNREACHABLE +} +static bool c_emit_function_decl(GenContext *c, Decl *fn) +{ + Signature *sig = &fn->func_decl.signature; + (void)c_emit_type_decl(c, typeget(sig->rtype)); + FOREACH(Decl *, d, sig->params) + { + c_emit_type_decl(c, d->type); + } + + if (fn->is_extern) + { + PRINTF("extern "); + } + else if (!fn->is_external_visible) + { + PRINTF("static "); + } + + PRINTF("%s ", c_type_name(c, typeget(sig->rtype))); + + fn->is_export = true; + scratch_buffer_set_extern_decl_name(fn, true); + PRINTF("%s(", scratch_buffer_to_string()); + FOREACH_IDX(i, Decl *, d, sig->params) + { + if (i != 0) PRINT(","); + PRINT(c_type_name(c, d->type)); + } + PRINT(");\n"); + return !fn->is_extern; +} + +static void c_emit_stmt_chain(GenContext *c, AstId current) +{ + while (current) + { + c_emit_stmt(c, ast_next(¤t)); + } +} + +static int c_create_label(GenContext *c) +{ + return ++c->id_gen; +} + +static int c_create_variable(GenContext *c) +{ + return ++c->id_gen; +} +static void c_emit_label(GenContext *c, int label) +{ + PRINTF("__C3_LABEL_%d:;\n", label); +} + +static void c_emit_goto_label(GenContext *c, int label) +{ + PRINTF("goto __C3_LABEL_%d;\n", label); +} + +static VariableId c_emit_temp(GenContext *c, CValue *value, Type *type) +{ + *value = (CValue) { .var = c_create_variable(c), .kind = CV_VALUE, .type = type_lowering(type) }; + return c->id_gen; +} +static void c_emit_const_expr(GenContext *c, CValue *value, Expr *expr) +{ + Type *t = type_lowering(expr->type); + switch (expr->const_expr.const_kind) + { + case CONST_FLOAT: + PRINTF("%s ___var_%d = %20.20g;\n", c_type_name(c, t), c_emit_temp(c, value, t), expr->const_expr.fxx.f); + return; + case CONST_INTEGER: + if (t == type_u128 || t == type_i128) + { + TODO + } + if (type_is_unsigned(t)) + { + PRINTF("%s ___var_%d = %llu;\n", c_type_name(c, t), c_emit_temp(c, value, t), expr->const_expr.ixx.i.low); + } + else + { + PRINTF("%s ___var_%d = %lld;\n", c_type_name(c, t), c_emit_temp(c, value, t), expr->const_expr.ixx.i.low); + } + return; + case CONST_BOOL: + PRINTF("bool ___var_%d = %s;\n", c_emit_temp(c, value, t), expr->const_expr.b ? "true" : "false"); + return; + case CONST_STRING: + PRINTF("%s ___var_%d = \"", c_type_name(c, t), c_emit_temp(c, value, t)); + for (ArraySize i = 0; i < expr->const_expr.bytes.len; i++) + { + char b = expr->const_expr.bytes.ptr[i]; + if (b >= ' ' || b < 127) + { + PRINTF("%c", b); + continue; + } + PRINTF("\\%d%d%d", b / 64, (b % 64) / 8, b % 8); + } + PRINT("\";\n"); + return; + case CONST_ENUM: + case CONST_ERR: + case CONST_BYTES: + break; + case CONST_POINTER: + break; + case CONST_TYPEID: + break; + case CONST_SLICE: + break; + case CONST_INITIALIZER: + break; + case CONST_REF: + break; + case CONST_UNTYPED_LIST: + case CONST_MEMBER: + UNREACHABLE + } + PRINT("/* CONST EXPR */\n"); +} +static void c_emit_expr(GenContext *c, CValue *value, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_ACCESS: + break; + case EXPR_ANYSWITCH: + break; + case EXPR_ASM: + break; + case EXPR_BENCHMARK_HOOK: + break; + case EXPR_BINARY: + break; + case EXPR_BITACCESS: + break; + case EXPR_BITASSIGN: + break; + case EXPR_BUILTIN: + break; + case EXPR_BUILTIN_ACCESS: + break; + case EXPR_CALL: + break; + case EXPR_CAST: + break; + case EXPR_CATCH_UNWRAP: + break; + case EXPR_COMPILER_CONST: + break; + case EXPR_COMPOUND_LITERAL: + break; + case EXPR_COND: + break; + case EXPR_CONST: + c_emit_const_expr(c, value, expr); + return; + case EXPR_TYPECALL: + break; + case EXPR_CT_AND_OR: + break; + case EXPR_CT_ARG: + break; + case EXPR_CT_APPEND: + break; + case EXPR_CT_CALL: + break; + case EXPR_CT_CASTABLE: + break; + case EXPR_CT_CONCAT: + break; + case EXPR_CT_DEFINED: + break; + case EXPR_CT_EVAL: + break; + case EXPR_CT_IDENT: + break; + case EXPR_CT_IS_CONST: + break; + case EXPR_DECL: + break; + case EXPR_DEFAULT_ARG: + break; + case EXPR_DESIGNATED_INITIALIZER_LIST: + break; + case EXPR_DESIGNATOR: + break; + case EXPR_EMBED: + break; + case EXPR_EXPRESSION_LIST: + break; + case EXPR_EXPR_BLOCK: + break; + case EXPR_FORCE_UNWRAP: + break; + case EXPR_GENERIC_IDENT: + break; + case EXPR_HASH_IDENT: + break; + case EXPR_IDENTIFIER: + break; + case EXPR_INITIALIZER_LIST: + break; + case EXPR_LAMBDA: + break; + case EXPR_LAST_FAULT: + break; + case EXPR_MACRO_BLOCK: + break; + case EXPR_MACRO_BODY: + break; + case EXPR_MACRO_BODY_EXPANSION: + break; + case EXPR_MEMBER_GET: + break; + case EXPR_NAMED_ARGUMENT: + break; + case EXPR_NOP: + break; + case EXPR_OPERATOR_CHARS: + break; + case EXPR_OPTIONAL: + break; + case EXPR_OTHER_CONTEXT: + break; + case EXPR_POINTER_OFFSET: + break; + case EXPR_POISONED: + break; + case EXPR_POST_UNARY: + break; + case EXPR_RETHROW: + break; + case EXPR_RETVAL: + break; + case EXPR_SLICE: + break; + case EXPR_SLICE_ASSIGN: + break; + case EXPR_SLICE_COPY: + break; + case EXPR_SPLAT: + break; + case EXPR_STRINGIFY: + break; + case EXPR_SUBSCRIPT: + break; + case EXPR_SUBSCRIPT_ADDR: + break; + case EXPR_SUBSCRIPT_ASSIGN: + break; + case EXPR_SWIZZLE: + break; + case EXPR_TERNARY: + break; + case EXPR_TEST_HOOK: + break; + case EXPR_TRY_UNWRAP: + break; + case EXPR_TRY_UNWRAP_CHAIN: + break; + case EXPR_TYPEID: + break; + case EXPR_TYPEID_INFO: + break; + case EXPR_TYPEINFO: + break; + case EXPR_UNARY: + break; + case EXPR_VASPLAT: + break; + } + PRINT("/* TODO EXPR */\n"); +} + +static void c_emit_jump_to_optional_exit(GenContext *c, int value) +{ + TODO +} +static int c_emit_load(GenContext *c, VariableId id) +{ + TODO +} + +static void c_value_fold_optional(GenContext *c, CValue *value) +{ + if (value->kind == CV_OPTIONAL_ADDRESS) + { + c_emit_jump_to_optional_exit(c, c_emit_load(c, value->optional)); + value->kind = CV_ADDRESS; + } +} + +static void c_emit_ignored_expr(GenContext *c, Expr *expr) +{ + CValue value; + + // For a standalone catch, we can ignore storing the value. + if (IS_OPTIONAL(expr)) + { + int discard_fail = c_create_label(c); + PUSH_CATCH_VAR_BLOCK(NULL, discard_fail); + c_emit_expr(c, &value, expr); + c_value_fold_optional(c, &value); + c_emit_goto_label(c, discard_fail); + c_emit_label(c, discard_fail); + POP_CATCH(); + return; + } + c_emit_expr(c, &value, expr); +} + +static void c_emit_expr_stmt(GenContext *c, Ast *ast) +{ + PRINT("/*EXPR*/\n"); + c_emit_ignored_expr(c, ast->expr_stmt); +} + +static void c_emit_local_decl(GenContext *c, Decl *decl, CValue *value) +{ + switch (decl->var.kind) + { + case VARDECL_CONST: + PRINT("/* LOCAL DECL */\n"); +// llvm_emit_local_static(c, decl, value); + return; + case VARDECL_LOCAL: + if (decl->var.is_static) + { + PRINT("/* LOCAL DECL */\n"); +// llvm_emit_local_static(c, decl, value); + return; + } + break; + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_PARAM_EXPR: + case VARDECL_GLOBAL: + case VARDECL_MEMBER: + case VARDECL_BITMEMBER: + UNREACHABLE; + case VARDECL_PARAM: + case VARDECL_PARAM_REF: + { + PRINT("/* LOCAL DECL */\n"); +/* + Expr *init_expr = decl->var.init_expr; + llvm_emit_expr(c, value, init_expr); + if (llvm_value_is_addr(value) || decl->var.is_written || decl->var.is_addr || llvm_use_accurate_debug_info(c)) + { + llvm_emit_and_set_decl_alloca(c, decl); + llvm_store_decl(c, decl, value); + return; + } + + decl->is_value = true; + decl->backend_value = value->value;*/ + return; + } + case VARDECL_UNWRAPPED: + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + return; + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + UNREACHABLE + } + + // Get the declaration and the LLVM type. + Type *var_type = type_lowering(decl->type); + + *value = (CValue) { .var = c_create_variable(c), .kind = CV_VALUE, .type = var_type }; + decl->backend_id = value->var; + PRINTF("%s ___var_%d;\n", c_type_name(c, var_type), value->var); + // Create optional storage + bool is_optional = IS_OPTIONAL(decl); + if (is_optional) + { + decl->var.optional_id = value->optional = c_create_variable(c); + PRINTF("void* ___var_f_%d;\n", value->optional); + } + + // Grab the init expression + Expr *init = decl->var.init_expr; + if (init) + { + CValue out; + c_emit_expr(c, &out, decl->var.init_expr); + if (out.kind == CV_OPTIONAL_ADDRESS) + { + PRINTF("___var_f_%d = __var_%d;\n", value->optional, out.optional); + } + else if (value->optional) + { + PRINTF("___var_f_%d = NULL;\n", value->optional); + } + PRINTF("___var_%d = ___var_%d;\n", value->var, out.var); + return; + } + + // If the variable has a no-init, then skip + if (decl->var.no_init) + { + return; + } + + if (is_optional) + { + PRINTF("___var_f_%d = NULL;\n", value->optional); + value->kind = CV_VALUE; + } + + PRINT("/* TODO ZERO INIT */\n"); + //llvm_store_zero(c, value); + //llvm_value_set(value, llvm_get_zero(c, var_type), var_type); +} + +static void c_emit_return(GenContext *c, Ast *stmt) +{ + Expr *expr = stmt->return_stmt.expr; + if (expr && expr->expr_kind == EXPR_OPTIONAL) + { + PRINT("/* RETURN */\n"); + /* + BEValue be_value; + llvm_emit_expr(c, &be_value, expr->inner_expr); + if (ast->return_stmt.cleanup_fail) + { + llvm_value_rvalue(c, &be_value); + LLVMValueRef error_out = llvm_emit_alloca_aligned(c, type_anyfault, "reterr"); + llvm_store_to_ptr(c, error_out, &be_value); + PUSH_DEFER_ERROR(error_out); + llvm_emit_statement_chain(c, ast->return_stmt.cleanup_fail); + POP_DEFER_ERROR(); + } + llvm_emit_return_abi(c, NULL, &be_value); + */ + return; + } + + PUSH_CATCH(); + + int error_return_block = 0; + int error_out = 0; + + if (!stmt->return_stmt.expr) + { + PRINT("return;\n"); + return; + } + PRINT("/* RETURN */\n"); + return; + /* + 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_anyfault, "reterr"); + c->catch = (OptionalCatch) { error_out, error_return_block }; + } + + bool has_return_value = ast->return_stmt.expr != NULL; + BEValue return_value = { 0 }; + if (has_return_value) + { + llvm_emit_expr(c, &return_value, ast->return_stmt.expr); + llvm_value_fold_optional(c, &return_value); + c->retval = return_value; + } + + POP_CATCH(); + + if (ast->return_stmt.cleanup || ast->return_stmt.cleanup_fail) + { + if (has_return_value) + { + if (llvm_temp_as_address(c, return_value.type)) + { + LLVMValueRef temp = llvm_emit_alloca_aligned(c, return_value.type, "ret$temp"); + llvm_store_to_ptr(c, temp, &return_value); + llvm_value_set_address_abi_aligned(&return_value, temp, return_value.type); + } + else + { + llvm_value_rvalue(c, &return_value); + } + } + llvm_emit_statement_chain(c, ast->return_stmt.cleanup); + } + + if (llvm_get_current_block_if_in_use(c)) + { + // Are we in an expression block? + if (!has_return_value) + { + llvm_emit_return_implicit(c); + } + else + { + llvm_emit_return_abi(c, &return_value, NULL); + } + } + if (error_return_block && LLVMGetFirstUse(LLVMBasicBlockAsValue(error_return_block))) + { + llvm_emit_block(c, error_return_block); + PUSH_DEFER_ERROR(error_out); + llvm_emit_statement_chain(c, ast->return_stmt.cleanup_fail); + POP_DEFER_ERROR(); + BEValue value; + llvm_value_set_address_abi_aligned(&value, error_out, type_anyfault); + llvm_emit_return_abi(c, NULL, &value); + }*/ +} + +static void c_emit_stmt(GenContext *c, Ast *stmt) +{ + if (!stmt) return; + switch (stmt->ast_kind) + { + case AST_POISONED: + UNREACHABLE + case AST_ASM_STMT: + break; + case AST_ASM_BLOCK_STMT: + break; + case AST_ASM_LABEL: + break; + case AST_ASSERT_STMT: + break; + case AST_BREAK_STMT: + break; + case AST_CASE_STMT: + break; + case AST_COMPOUND_STMT: + PRINT(" {\n"); + c_emit_stmt_chain(c, stmt->compound_stmt.first_stmt); + PRINT(" }\n"); + return; + case AST_CONTINUE_STMT: + break; + case AST_CT_ASSERT: + break; + case AST_CT_ECHO_STMT: + break; + case AST_CT_ELSE_STMT: + break; + case AST_CT_FOREACH_STMT: + case AST_CT_FOR_STMT: + case AST_CT_IF_STMT: + case AST_CT_SWITCH_STMT: + UNREACHABLE + case AST_DECLARE_STMT: + { + CValue value; + c_emit_local_decl(c, stmt->declare_stmt, &value); + return; + } + case AST_DECLS_STMT: + break; + case AST_DEFAULT_STMT: + break; + case AST_DEFER_STMT: + break; + case AST_EXPR_STMT: + c_emit_expr_stmt(c, stmt); + return; + case AST_FOR_STMT: + PRINT("/* FOR */\n"); + break; + case AST_FOREACH_STMT: + break; + case AST_IF_CATCH_SWITCH_STMT: + break; + case AST_IF_STMT: + break; + case AST_NOP_STMT: + PRINT(";\n"); + return; + case AST_RETURN_STMT: + c_emit_return(c, stmt); + return; + case AST_BLOCK_EXIT_STMT: + break; + case AST_SWITCH_STMT: + break; + case AST_NEXTCASE_STMT: + break; + case AST_CONTRACT: + break; + case AST_CONTRACT_FAULT: + break; + } + PRINT("/* TODO */\n"); +} +static void c_emit_function(GenContext *c, Decl *fn) +{ + Signature *sig = &fn->func_decl.signature; + if (!fn->is_external_visible) + { + PRINTF("static "); + } + + PRINTF("%s ", c_type_name(c, typeget(sig->rtype))); + + fn->is_export = true; + scratch_buffer_set_extern_decl_name(fn, true); + PRINTF("%s(", scratch_buffer_to_string()); + FOREACH_IDX(i, Decl *, d, sig->params) + { + if (i != 0) PRINT(","); + PRINTF("%s ___arg_%d__", c_type_name(c, d->type), i); + } + PRINT(") {\n"); + c_emit_stmt(c, astptrzero(fn->func_decl.body)); + PRINT("}\n"); +} + +static GenContext *c_gen_module(Module *module, int num) +{ + if (!vec_size(module->units)) return NULL; + // if (compiler.build.emit_stdlib == EMIT_STDLIB_OFF && module_is_stdlib(module)) return NULL; + + scratch_buffer_clear(); + scratch_buffer_printf("temp%d.c", num); + FILE* f = fopen(scratch_buffer_to_string(), "wb"); + if (!f) error_exit("Failed to output module."); + fputs("#include \n", f); + fputs("#include \n", f); + fputs("#include \n", f); + + bool has_elements = false; + GenContext *c = cmalloc(sizeof(GenContext)); + *c = (GenContext) { .file = f }; + htable_init(&c->gen_decl, 1024); + htable_init(&c->gen_decl, 1024); + + bool only_used = strip_unused(); + FOREACH(CompilationUnit *, unit, module->units) + { + FOREACH(Decl *, method, unit->methods) + { + if (only_used && !method->is_live) continue; + has_elements |= c_emit_function_decl(c, method); + } + FOREACH(Decl *, func, unit->functions) + { + if (only_used && !func->is_live) continue; + has_elements |= c_emit_function_decl(c, func); + } + + FOREACH(Decl *, type_decl, unit->types) + { + if (only_used && !type_decl->is_live) continue; + c_emit_type_decl(c, type_decl->type); + } + } + FOREACH(CompilationUnit *, unit, module->units) + { + FOREACH(Decl *, method, unit->methods) + { + if (only_used && !method->is_live) continue; + has_elements = true; + c_emit_function(c, method); + } + FOREACH(Decl *, func, unit->functions) + { + if (only_used && !func->is_live) continue; + has_elements = true; + c_emit_function(c, func); + } + } +/* + + FOREACH(Decl *, enum_decl, unit->enums) + { + if (only_used && !enum_decl->is_live) continue; + llvm_emit_type_decls(gen_context, enum_decl); + } + + FOREACH(Decl *, func, unit->functions) + { + if (only_used && !func->is_live) continue; + if (func->func_decl.attr_test) + { + if (!compiler.build.testing) continue; + vec_add(module->tests, func); + } + if (func->func_decl.attr_benchmark) + { + if (!compiler.build.benchmarking) continue; + vec_add(module->benchmarks, func); + } + llvm_emit_function_decl(gen_context, func); + } + + + FOREACH(Decl *, func, unit->lambdas) + { + if (only_used && !func->is_live) continue; + has_elements = true; + llvm_emit_function_decl(gen_context, func); + } + + if (compiler.build.type != TARGET_TYPE_TEST && compiler.build.type != TARGET_TYPE_BENCHMARK && unit->main_function && unit->main_function->is_synthetic) + { + has_elements = true; + llvm_emit_function_decl(gen_context, unit->main_function); + } +*/ + +/* + FOREACH(CompilationUnit *, unit, module->units) + { + gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; + gen_context->debug.file = (DebugFile){ + .debug_file = unit->llvm.debug_file, + .file_id = unit->file->file_id }; + + FOREACH(Decl *, var, unit->vars) + { + if (only_used && !var->is_live) continue; + has_elements = true; + llvm_get_ref(gen_context, var); + } + + FOREACH(Decl *, var, unit->vars) + { + if (only_used && !var->is_live) continue; + has_elements = true; + llvm_emit_global_variable_init(gen_context, var); + } + + FOREACH(Decl *, decl, unit->functions) + { + if (decl->func_decl.attr_test && !compiler.build.testing) continue; + if (decl->func_decl.attr_benchmark && !compiler.build.benchmarking) continue; + if (only_used && !decl->is_live) continue; + if (decl->func_decl.body) + { + has_elements = true; + llvm_emit_function_body(gen_context, decl); + } + } + + FOREACH(Decl *, func, unit->lambdas) + { + if (only_used && !func->is_live) continue; + has_elements = true; + llvm_emit_function_body(gen_context, func); + } + + if (compiler.build.type != TARGET_TYPE_TEST && compiler.build.type != TARGET_TYPE_BENCHMARK && unit->main_function && unit->main_function->is_synthetic) + { + has_elements = true; + llvm_emit_function_body(gen_context, unit->main_function); + } + + FOREACH(Decl *, decl, unit->methods) + { + if (only_used && !decl->is_live) continue; + if (!decl->func_decl.body) continue; + has_elements = true; + llvm_emit_function_body(gen_context, decl); + } + + gencontext_end_file_emit(gen_context, unit); + + } + + llvm_emit_dynamic_functions(gen_context, gen_context->dynamic_functions); + + llvm_emit_constructors_and_destructors(gen_context); + + if (llvm_use_debug(gen_context)) + { + LLVMDIBuilderFinalize(gen_context->debug.builder); + LLVMDisposeDIBuilder(gen_context->debug.builder); + } + + // If it's in test or benchmark, then we want to serialize the IR before it is optimized. + if (compiler.build.test_output || compiler.build.benchmark_output) + { + gencontext_print_llvm_ir(gen_context); + gencontext_verify_ir(gen_context); + } + if (!has_elements) return NULL; + return gen_context; +*/ + fclose(f); + if (!has_elements) + { + scratch_buffer_clear(); + scratch_buffer_printf("temp%d.c", num); + file_delete_file(scratch_buffer_to_string()); + } + return c; +} + +void **c_gen(Module** modules, unsigned module_count) +{ + if (!module_count) return NULL; + void **gen_contexts = NULL; + for (unsigned i = 0; i < module_count; i++) + { + c_gen_module(modules[i], i); + } + return NULL; +} \ No newline at end of file diff --git a/src/compiler/c_codegen_internal.h b/src/compiler/c_codegen_internal.h new file mode 100644 index 000000000..6fb01f9ec --- /dev/null +++ b/src/compiler/c_codegen_internal.h @@ -0,0 +1 @@ +#include "codegen_internal.h" diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 6b26e18ae..d01426207 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -468,6 +468,9 @@ void compiler_compile(void) switch (compiler.build.backend) { + case BACKEND_C: + gen_contexts = c_gen(modules, module_count); + error_exit("Unfinished C backend!"); case BACKEND_LLVM: #if LLVM_AVAILABLE gen_contexts = llvm_gen(modules, module_count); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9db64fcd2..27a99edfb 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -439,6 +439,7 @@ typedef struct VarDecl_ { // Variable void *optional_ref; + int optional_id; int tb_optional_reg; }; }; @@ -613,6 +614,7 @@ typedef struct Decl_ union { void *backend_ref; + int backend_id; int tb_register; void *backend_value; void *tb_symbol; @@ -2120,6 +2122,7 @@ CastKind cast_to_bool_kind(Type *type); const char *llvm_codegen(void *context); const char *tilde_codegen(void *context); +void **c_gen(Module** modules, unsigned module_count); void **llvm_gen(Module** modules, unsigned module_count); void **tilde_gen(Module** modules, unsigned module_count); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 1943c92c8..0eb2d4542 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -347,7 +347,6 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) } AlignSize member_alignment; if (!sema_set_abi_alignment(context, member->type, &member_alignment)) return false; - if (!sema_check_struct_holes(context, decl, member)) return false; ByteSize member_size = type_size(member->type); diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 2a771e5b6..d1ea7a0ef 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -208,15 +208,16 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) decl->unit = unit; switch (decl->decl_kind) { - case DECL_POISONED: case DECL_ENUM_CONSTANT: case DECL_FAULTVALUE: - case DECL_IMPORT: - case DECL_LABEL: - case DECL_CT_ASSERT: - case DECL_CT_ECHO: case DECL_DECLARRAY: case DECL_ERASED: + case DECL_LABEL: + UNREACHABLE + case DECL_POISONED: + case DECL_IMPORT: + case DECL_CT_ASSERT: + case DECL_CT_ECHO: case DECL_FNTYPE: case DECL_CT_INCLUDE: case DECL_CT_EXEC: