#ifndef CCBGENERIC_H #define CCBGENERIC_H // TODO: Make the parser header consistent with my git version #include "ccb.h" #ifdef CCBGENERIC_IMPLEMENTATION //#define CCB_TARGET_ENVPREFIX "CCB_" static int ccb_target_fam;// = -1; static int ccb_target_wordsize_val;// = -1; static int ccb_target_callconv_val;// = -1; static void ccb_target_gen_rv_addi(ccb_t* ccb, char* destr, char* srcr, int offset); static void ccb_target_gen_rv_loadstore(ccb_t* ccb, char* op, char* spec, char* r, char* baser, int offset, char* tmpr); static void ccb_target_gen_rv_load(ccb_t* ccb, char* spec, char* r, char* baser, int offset, char* tmpr); static void ccb_target_gen_rv_store(ccb_t* ccb, char* spec, char* r, char* baser, int offset, char* tmpr); void ccb_target_init(ccb_t* ccb) { const char* x; //fprintf(stderr, "XXX: INITIALISING TARGET\n"); ccb_target_fam = -1; ccb_target_wordsize_val = -1; ccb_target_callconv_val = -1; x = getenv(/*CCB_TARGET_ENVPREFIX*/ "CCB_FAMILY"); //fprintf(stderr, "XXX: INITIALISING TARGET ...\n"); //fprintf(stderr, "XXX: INITIALISING TARGET GOT '%s'\n", x); if (x != NULL) { //printf("Decoding target value '%s'\n", x); //fprintf(stderr, "XXX: INITIALISING TARGET A\n"); if (strcmp(x, "x86") == 0 || strcmp(x, "X86") == 0) { ccb_target_fam = CCB_ARCH_FAMILY_X86; } else if (strcmp(x, "arm") == 0 || strcmp(x, "ARM") == 0) { ccb_target_fam = CCB_ARCH_FAMILY_ARM; } else if (strcmp(x, "risc-v") == 0 || strcmp(x, "RISC-V") == 0 || strcmp(x, "riscv") == 0 || strcmp(x, "RISCV") == 0) { //printf("Is RV\n"); ccb_target_fam = CCB_ARCH_FAMILY_RISCV; } else if (strcmp(x, "generic") == 0 || strcmp(x, "GENERIC") == 0) { ccb_target_fam = CCB_ARCH_FAMILY_GENERIC; } else if (strcmp(x, "gen1") == 0 || strcmp(x, "GEN1") == 0) { ccb_target_fam = CCB_ARCH_FAMILY_GEN1; } else { ccb_compile_error(ccb, "Invalid value for environment variable (%s=\"%s\")", /* CCB_TARGET_ENVPREFIX */ "CCB_FAMILY", x); } } else { //fprintf(stderr, "XXX: INITIALISING TARGET B\n"); ccb_target_fam = CCB_ARCH_FAMILY_X86; // GENERIC; // X86; // TODO: Guess based on build environment or use compiler flag? (Maybe use GENERIC by default in the future?) } //fprintf(stderr, "XXX: HALF INITIALISING TARGET\n"); x = getenv(/* CCB_TARGET_ENVPREFIX */ "CCB_CALLCONV"); if (x != NULL) { if (strcmp(x, "standard") == 0 || strcmp(x, "STANDARD") == 0) { ccb_target_callconv_val = CCB_TARGET_CALLCONV_STANDARD; } else if (strcmp(x, "windows") == 0 || strcmp(x, "WINDOWS") == 0) { ccb_target_callconv_val = CCB_TARGET_CALLCONV_WINDOWS; } else { ccb_compile_error(ccb, "Invalid value for environment variable (%s=\"%s\")", /* CCB_TARGET_ENVPREFIX */ "CCB_CALLCONV", x); } } else { ccb_target_callconv_val = CCB_TARGET_CALLCONV_STANDARD; } x = getenv(/* CCB_TARGET_ENVPREFIX */ "CCB_WORDSIZE"); if (x != NULL) { printf("Decoding word size value '%s'\n", x); if (strcmp(x, "16") == 0) { ccb_target_wordsize_val = 16; } else if (strcmp(x, "32") == 0) { ccb_target_wordsize_val = 32; } else if (strcmp(x, "64") == 0) { printf("Is 64-bit\n"); ccb_target_wordsize_val = 64; } else { ccb_compile_error(ccb, "Invalid value for environment variable (%s=\"%s\"): Only 16, 32 and 64-bit word sizes are available", /* CCB_TARGET_ENVPREFIX */ "CCB_ARCH_WORDSIZE", x); } } //printf("Got family id %ld\n", ccb_target_fam); switch (ccb_target_fam) { case CCB_ARCH_FAMILY_X86: if (ccb_target_wordsize_val == -1) { ccb_target_wordsize_val = 64; } break; case CCB_ARCH_FAMILY_ARM: case CCB_ARCH_FAMILY_RISCV: if (ccb_target_wordsize_val == -1) { ccb_target_wordsize_val = 64; } if (ccb_target_wordsize_val < 32) { ccb_compile_error(ccb, "Invalid value for environment variable (%s=\"%s\"): Not available in the ARM or RISC-V targets", /* CCB_TARGET_ENVPREFIX */ "CCB_ARCH_WORDSIZE", x); } break; case CCB_ARCH_FAMILY_GEN1: if (ccb_target_wordsize_val == -1) { ccb_target_wordsize_val = 64; } if (ccb_target_wordsize_val != 64) { ccb_compile_error(ccb, "Invalid value for environment variable (%s=\"%s\"): Not available in the GEN1 target", /* CCB_TARGET_ENVPREFIX */ "CCB_WORDSIZE", x); } break; case CCB_ARCH_FAMILY_GENERIC: if (ccb_target_wordsize_val == -1) { ccb_target_wordsize_val = 64; } break; default: ccb_compile_error(ccb, "Internal Error (missing case?)"); } //fprintf(stderr, "XXX: DONE INITIALISING TARGET\n"); } int ccb_target_family(ccb_t* ccb) { switch (ccb_target_fam) { case CCB_ARCH_FAMILY_X86: case CCB_ARCH_FAMILY_ARM: case CCB_ARCH_FAMILY_RISCV: case CCB_ARCH_FAMILY_GENERIC: case CCB_ARCH_FAMILY_GEN1: return ccb_target_fam; default: ccb_compile_error(ccb, "Internal Error: arch_init() hasn't completed yet"); } } int ccb_target_wordsize(ccb_t* ccb) { return ccb_target_wordsize_val; } int ccb_target_wordbytes(ccb_t* ccb) { return ccb_target_wordsize(ccb)/8; } int ccb_target_callconv(ccb_t* ccb) { return ccb_target_callconv_val; } size_t ccb_target_type_size_char(ccb_t* ccb) { return 1; } size_t ccb_target_type_size_short(ccb_t* ccb) { return 2; } size_t ccb_target_type_size_int(ccb_t* ccb) { if (ccb_target_wordsize(ccb) >= 32) { return 4; } else { return 2; } } size_t ccb_target_type_size_long(ccb_t* ccb) { if (ccb_target_wordsize(ccb) >= 64) { return 8; } else { return 4; } } size_t ccb_target_type_size_llong(ccb_t* ccb) { return 8; } size_t ccb_target_type_size_float(ccb_t* ccb) { return 4; } size_t ccb_target_type_size_double(ccb_t* ccb) { return 8; } size_t ccb_target_type_size_ldouble(ccb_t* ccb) { return 8; } size_t ccb_target_type_size_pointer(ccb_t* ccb) { return ccb_target_wordsize(ccb) / 8; } size_t ccb_target_alignment(ccb_t* ccb) { return 1; } int ccb_target_callregisters(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return (ccb_target_callconv(ccb) == CCB_TARGET_CALLCONV_WINDOWS) ? 4 : 6; // TODO: This only applies to 64-bit mode case CCB_ARCH_FAMILY_ARM: return 8; // TODO: ARM only has 4 on 32-bit targets case CCB_ARCH_FAMILY_RISCV: return 8; // Pretty sure this is the same for all RISC-V ISAs case CCB_ARCH_FAMILY_GENERIC: return 6; // Currently... case CCB_ARCH_FAMILY_GEN1: return 4; // With standard-ish ABI, might use about 8-ish in the future default: ccb_compile_error(ccb, "Target Error: arch_callregisters() got unknown arch"); return -1; // Unreachable } } const char* ccb_target_callregister(ccb_t* ccb, int idx) { if (idx < 0 || idx >= ccb_target_callregisters(ccb)) { ccb_compile_error(ccb, "Target Error: arch_callregister(cc,%d) received bad argument", idx); } switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: if (ccb_target_callconv(ccb) == CCB_TARGET_CALLCONV_WINDOWS) { switch (idx) { case 0: return "rcx"; case 1: return "rdx"; case 2: return "r8"; case 3: return "r9"; } } else { switch (idx) { case 0: return "rdi"; case 1: return "rsi"; case 2: return "rdx"; case 3: return "rcx"; case 4: return "r8"; case 5: return "r9"; } } ccb_compile_error(ccb, "Target Error: Register lookup failed"); case CCB_ARCH_FAMILY_ARM: switch (idx) { case 0: return "x0"; case 1: return "x1"; case 2: return "x2"; case 3: return "x3"; case 4: return "x4"; case 5: return "x5"; case 6: return "x6"; case 7: return "x7"; } ccb_compile_error(ccb, "Target Error: Register lookup failed"); case CCB_ARCH_FAMILY_RISCV: switch (idx) { case 0: return "a0"; // Also known as 'x10' case 1: return "a1"; case 2: return "a2"; case 3: return "a3"; case 4: return "a4"; case 5: return "a5"; case 6: return "a6"; case 7: return "a7"; // .. 'x17' } ccb_compile_error(ccb, "Target Error: Register lookup failed"); case CCB_ARCH_FAMILY_GENERIC: switch (idx) { case 0: return "r7"; case 1: return "r6"; case 2: return "r2"; case 3: return "r1"; case 4: return "r8"; case 5: return "r9"; } ccb_compile_error(ccb, "Target Error: Register lookup failed"); case CCB_ARCH_FAMILY_GEN1: switch (idx) { case 0: return "$r0"; case 1: return "$r1"; case 2: return "$r2"; case 3: return "$r3"; } ccb_compile_error(ccb, "Target Error: Register lookup failed"); default: ccb_compile_error(ccb, "Target Error: Register lookup failed"); return "ERROR?"; // Unreachable } } const char* ccb_target_r0(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return "rax"; case CCB_ARCH_FAMILY_GEN1: return "$r0"; case CCB_ARCH_FAMILY_GENERIC: return "r0"; case CCB_ARCH_FAMILY_RISCV: return "a0"; case CCB_ARCH_FAMILY_ARM: return "x0"; default: return "todo"; } } const char* ccb_target_r1(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return "rcx"; case CCB_ARCH_FAMILY_GEN1: return "$r1"; case CCB_ARCH_FAMILY_GENERIC: return "r1"; case CCB_ARCH_FAMILY_RISCV: return "a1"; case CCB_ARCH_FAMILY_ARM: return "x1"; default: return "todo"; } } const char* ccb_target_r15(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return "r15"; case CCB_ARCH_FAMILY_GEN1: return "$rf"; case CCB_ARCH_FAMILY_GENERIC: return "r15"; case CCB_ARCH_FAMILY_RISCV: return "t0"; default: return "todo"; } } const char* ccb_target_sp(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return "rsp"; case CCB_ARCH_FAMILY_GEN1: return "$rstack"; case CCB_ARCH_FAMILY_RISCV: return "sp"; // Also known as 'x2' default: return "r4"; } } const char* ccb_target_bp(ccb_t* ccb) { switch (ccb_target_family(ccb)) { case CCB_ARCH_FAMILY_X86: return "rbp"; case CCB_ARCH_FAMILY_GEN1: return "$rbase"; case CCB_ARCH_FAMILY_RISCV: return "x8";//"fp"; // Also known as 's0' (or `x8`) default: return "r5"; } } static int ccb_target_gen_asmfmt_cached = 0; int ccb_target_asmfmt(ccb_t* ccb) { if (ccb_target_gen_asmfmt_cached == 0) { const char* val = getenv(/* CCB_TARGET_ENVPREFIX */ "CCB_ASMFMT"); if (val == NULL) { ccb_target_gen_asmfmt_cached = CCB_TARGET_ASMFMT_GAS; } else if (strcmp(val, "") == 0 || strcmp(val, "gas") == 0) { ccb_target_gen_asmfmt_cached = CCB_TARGET_ASMFMT_GAS; } else if (strcmp(val, "fasm") == 0) { ccb_target_gen_asmfmt_cached = CCB_TARGET_ASMFMT_FASM; } else if (strcmp(val, "nasm") == 0) { ccb_target_gen_asmfmt_cached = CCB_TARGET_ASMFMT_NASM; } else if (strcmp(val, "raw") == 0) { ccb_target_gen_asmfmt_cached = CCB_TARGET_ASMFMT_RAW; } else { fprintf(stderr, "Invalid value for CC_ASMFMT (\"%s\")\n", val); return -1; } } return ccb_target_gen_asmfmt_cached; } static int ccb_target_binfmt_cached = 0; int ccb_target_binfmt(ccb_t* ccb) { if (ccb_target_binfmt_cached == 0) { const char* val = getenv(/* CCB_TARGET_ENVPREFIX */ "CCB_BINFMT"); if (val == NULL || strcmp(val, "") == 0 || strcmp(val, "elf") == 0) { ccb_target_binfmt_cached = CCB_TARGET_BINFMT_ELF; } else if (strcmp(val, "flat") == 0) { ccb_target_binfmt_cached = CCB_TARGET_BINFMT_FLAT; } else { fprintf(stderr, "Invalid value for CC_BINFMT (\"%s\")\n", val); return -1; } } return ccb_target_binfmt_cached; } /* static const char *registers[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" }; */ static void ccb_target_gen_expression(ccb_t* ccb, ccb_ast_t*); /** Conceptually the same as ccb_target_gen_expression, except it will pop the result off the stack. */ static void ccb_target_gen_statement(ccb_t* ccb, ccb_ast_t*); static void ccb_target_gen_declaration_initialization(ccb_t* ccb, ccb_list_t*, int); #ifdef _ZCC #define ccb_target_gen_emit(...) do{fprintf(ccb->output, __VA_ARGS__); fprintf(ccb->output, "\n"); fflush(ccb->output);}while(0) #define ccb_target_gen_emit_inline(...) do{fprintf(ccb->output, __VA_ARGS__); fprintf(ccb->output, "\n"); fflush(ccb->output);}while(0) /* TODO: Better string handling... #define ccb_target_gen_emit(...) ccb_target_gen_emit_impl(ccb, __LINE__, "\t" __VA_ARGS__) */ #else #define ccb_target_gen_emit(...) ccb_target_gen_emit_impl(ccb, __LINE__, __VA_ARGS__) #define ccb_target_gen_emit_inline(...) ccb_target_gen_emit_impl(ccb, __LINE__, __VA_ARGS__) #endif #define ccb_target_gen_push(X) ccb_target_gen_push_ (ccb, X, __LINE__) #define ccb_target_gen_pop(X) ccb_target_gen_pop_ (ccb, X, __LINE__) #define ccb_target_gen_drop() ccb_target_gen_drop_ (ccb, __LINE__) #define ccb_target_gen_push_xmm(X) ccb_target_gen_push_xmm_(ccb, X, __LINE__) #define ccb_target_gen_pop_xmm(X) ccb_target_gen_pop_xmm_ (ccb, X, __LINE__) static int ccb_target_gen_stack = 0; static char* ccb_target_gen_label_break = NULL; static char* ccb_target_gen_label_continue = NULL; //static char* ccb_target_gen_label_break_store = NULL; //static char* ccb_target_gen_label_continue_store = NULL; static char* ccb_target_gen_label_switch = NULL; //static char* ccb_target_gen_label_switch_store = NULL; #ifdef _ZCC #define ccb_target_gen_emit_impl(ccb,ln,...) do{fprintf(ccb->output, __VA_ARGS__); fprintf(ccb->output, "\n"); } while(0) #else void ccb_target_gen_emit_impl(ccb_t* ccb, int line, const char* fmt, ...) { va_list args; va_start(args, fmt); int col = vfprintf(ccb->output, fmt, args); va_end(args); for (const char* p = fmt; *p; p++) if (*p == '\t') col += 8 - 1; col = (40 - col) > 0 ? (40 - col) : 2; if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { fprintf(ccb->output, "%*c % 4d %d\n", col, ';', line, ccb_target_gen_stack); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { // 32-bit: fprintf(ccb->output, "%*c % 4d %d\n", col, '@', line, ccb_target_gen_stack); fprintf(ccb->output, " // %d %d\n", line, ccb_target_gen_stack); } else { fprintf(ccb->output, "%*c % 4d %d\n", col, '#', line, ccb_target_gen_stack); } } #endif //static void ccb_target_gen_jump_save(ccb_t* ccb, char* lbreak, char* lcontinue) { #define ccb_target_gen_jump_save(ccb,lbreak,lcontinue) char* ccb_target_gen_label_break_store = ccb_target_gen_label_break; char* ccb_target_gen_label_continue_store = ccb_target_gen_label_continue; ccb_target_gen_label_break = lbreak; ccb_target_gen_label_continue = lcontinue //static void ccb_target_gen_jump_restore(ccb_t* ccb) { #define ccb_target_gen_jump_restore(ccb) do { ccb_target_gen_label_break = ccb_target_gen_label_break_store; ccb_target_gen_label_continue = ccb_target_gen_label_continue_store; } while(0); static int ccb_target_regcode(ccb_t* ccb, const char* reg) { if (!strcmp(reg, "rax")) { return 0; } else if (!strcmp(reg, "rcx")) { return 1; } else if (!strcmp(reg, "rdx")) { return 2; } else if (!strcmp(reg, "rbx")) { return 3; } else if (!strcmp(reg, "rsi")) { return 6; } else if (!strcmp(reg, "rdi")) { return 7; } else { //fprintf(stderr, "WARNING: Unimplemented regcode '%s'\n", reg); return -1; } } static void ccb_target_gen_push_(ccb_t* ccb, const char* reg, int line) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit_impl(ccb, line, "\twrite32 $rsp, %s, -8", reg); // TODO: Fix this mess. ccb_target_gen_emit_impl(ccb, line, "\txor $rscratch, $rscratch, $rscratch"); ccb_target_gen_emit_impl(ccb, line, "\taddimm $rscratch, $rscratch, 32"); ccb_target_gen_emit_impl(ccb, line, "\tshrz $rscratch, %s, $rscratch", reg); ccb_target_gen_emit_impl(ccb, line, "\twrite32 $rsp, $rscratch, -4"); ccb_target_gen_emit_impl(ccb, line, "\taddimm $rsp, -8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_target_wordsize(ccb) == 32) { ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, -4"); ccb_target_gen_emit_impl(ccb, line, "\tsw %s, 0(sp)", reg); } else { ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, -8"); ccb_target_gen_emit_impl(ccb, line, "\tsd %s, 0(sp)", reg); } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { // TODO: Optimise ARM64 stack operations // (for some horrible reason, you can't use SP as a simple word-sized stack :s) // For details: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop ccb_target_gen_emit_impl(ccb, line, "\tstr %s, [sp, #-16]", reg); ccb_target_gen_stack += (ccb_target_wordsize(ccb)/8); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_impl(ccb, line, "\tpushr %s", reg); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit_impl(ccb, line, "\tpush %s", reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { int regcode = ccb_target_regcode(ccb, reg); if (regcode < 0) { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x?? ; TODO: pop %s", reg); } else { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x%x ; push %s", 0x50 + regcode, reg); } } else { ccb_target_gen_emit_impl(ccb, line, "\tpush %%%s", reg); } ccb_target_gen_stack += (ccb_target_wordsize(ccb)/8); } static void ccb_target_gen_pop_(ccb_t* ccb, const char* reg, int line) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit_impl(ccb, line, "\tread32 %s, $rsp, 4", reg); ccb_target_gen_emit_impl(ccb, line, "\txor $rscratch, $rscratch, $rscratch"); ccb_target_gen_emit_impl(ccb, line, "\taddimm $rscratch, $rscratch, 32"); ccb_target_gen_emit_impl(ccb, line, "\tshl %s, %s, $rscratch", reg, reg); ccb_target_gen_emit_impl(ccb, line, "\tread32 %rscratch, $rsp, 0"); ccb_target_gen_emit_impl(ccb, line, "\tor %s, %s, %rscratch", reg, reg); ccb_target_gen_emit_impl(ccb, line, "\taddimm $rsp, $rsp, 8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_target_wordsize(ccb) == 32) { ccb_target_gen_emit_impl(ccb, line, "\tlw %s, 0(sp)", reg); ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, 4"); } else { ccb_target_gen_emit_impl(ccb, line, "\tld %s, 0(sp)", reg); ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, 8"); } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { // TODO: Optimise ARM64 stack operations // (for some horrible reason, you can't use SP as a simple word-sized stack :s) ccb_target_gen_emit_impl(ccb, line, "\tldr %s, [sp], #16", reg); ccb_target_gen_stack -= (ccb_target_wordsize(ccb)/8); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_impl(ccb, line, "\tpopr %s", reg); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit_impl(ccb, line, "\tpop %s", reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { int regcode = ccb_target_regcode(ccb, reg); if (regcode < 0) { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x?? ; TODO: pop %s", reg); } else { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x%x ; pop %s", 0x58 + regcode, reg); } } else { ccb_target_gen_emit_impl(ccb, line, "\tpop %%%s", reg); } ccb_target_gen_stack -= (ccb_target_wordsize(ccb)/8); } static void ccb_target_gen_drop_(ccb_t* ccb, int line) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit_impl(ccb, line, "\taddimm $rsp, $rsp, 8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_target_wordsize(ccb) == 32) { //ccb_target_gen_emit_impl(ccb, line, "\tlw %s, 0(sp)", reg); ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, 4"); } else { //ccb_target_gen_emit_impl(ccb, line, "\tld %s, 0(sp)", reg); ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, 8"); } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { // TODO: Optimise ARM64 stack operations // (for some horrible reason, you can't use SP as a simple word-sized stack :s) //cb_target_gen_emit_impl(ccb, line, "\tldr %s, [sp], #16", reg); //ccb_target_gen_stack -= (ccb_target_wordsize(ccb)/8); ccb_compile_error(ccb, "TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { //ccb_target_gen_emit_impl(ccb, line, "\tpopr %s", reg); ccb_compile_error(ccb, "TODO"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit_impl(ccb, line, "\tadd rsp, %d", ccb_target_wordbytes(ccb)); // TODO: Adapt push/pop/drop to 32 bit sp if needed.. } /*else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { int regcode = ccb_target_regcode(ccb, reg); if (regcode < 0) { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x?? ; TODO: pop %s", reg); } else { ccb_target_gen_emit_impl(ccb, line, "\tdata8 0x%x ; pop %s", 0x58 + regcode, reg); } }*/ else { //ccb_target_gen_emit_impl(ccb, line, "\tpop %%%s", reg); ccb_target_gen_emit_impl(ccb, line, "\taddq $%d, %%rsp", ccb_target_wordbytes(ccb)); } ccb_target_gen_stack -= (ccb_target_wordsize(ccb)/8); } static void ccb_target_gen_push_xmm_(ccb_t* ccb, int r, int line) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_impl(ccb, line, "\tsubrc r4, 8"); ccb_target_gen_emit_impl(ccb, line, "\tsetrmf r4, f%d", r); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, -8"); ccb_target_gen_emit_impl(ccb, line, "\tfsd fa%d, 0(sp)", r); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit_impl(ccb, line, "\tsub rsp, 8"); ccb_target_gen_emit_impl(ccb, line, "\tmovsd [rsp], xmm%d", r); } else { ccb_target_gen_emit_impl(ccb, line, "\tsub $8, %%rsp"); ccb_target_gen_emit_impl(ccb, line, "\tmovsd %%xmm%d, (%%rsp)", r); } ccb_target_gen_stack += 8; } static void ccb_target_gen_pop_xmm_(ccb_t* ccb, int r, int line) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_impl(ccb, line, "\tsetfrm f%d, r4", r); ccb_target_gen_emit_impl(ccb, line, "\taddrc r4, 8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit_impl(ccb, line, "\tfld fa%d, 0(sp)", r); ccb_target_gen_emit_impl(ccb, line, "\taddi sp, sp, 8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit_impl(ccb, line, "\tmovsd xmm%d, [rsp]", r); ccb_target_gen_emit_impl(ccb, line, "\tadd rsp, 8"); } else { ccb_target_gen_emit_impl(ccb, line, "\tmovsd (%%rsp), %%xmm%d", r); ccb_target_gen_emit_impl(ccb, line, "\tadd $8, %%rsp"); } ccb_target_gen_stack -= 8; } static const char* ccb_target_gen_register_integer(ccb_t* ccb, ccb_data_type_t* type, char r) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { // TODO... switch (type->size) { case 1: return (r == 'a') ? "r0x8" : "r1x8"; case 2: return (r == 'a') ? "r0x16" : "r1x16"; case 4: return (r == 'a') ? "v0" : "v1"; case 8: return (r == 'a') ? "v0x64" : "v1x64"; } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { switch (type->size) { case 1: return (r == 'a') ? "r0x8" : "r1x8"; case 2: return (r == 'a') ? "r0x16" : "r1x16"; case 4: return (r == 'a') ? "r0x32" : "r1x32"; case 8: return (r == 'a') ? "r0" : "r1"; } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { return (r == 'a') ? "a0" : "a1"; } else { switch (type->size) { case 1: return (r == 'a') ? "al" : "cl"; case 2: return (r == 'a') ? "ax" : "cx"; case 4: return (r == 'a') ? "eax" : "ecx"; case 8: return (r == 'a') ? "rax" : "rcx"; } } ccb_compile_error(ccb, "Unexpected operand to ccb_target_gen_register_integer"); return ""; // Unreachable? } static const char* ccb_target_gen_loadstorespec(ccb_t* ccb, ccb_data_type_t* type, bool isstore) { if (type->type == CCB_TYPE_FLOAT && !type->sign) { printf("WARNING: float type has sign set to false, correcting this...\n"); type->sign = true; } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { switch (type->size) { case 1: return isstore ? "b" : (type->sign ? "b" : "bu"); case 2: return isstore ? "h" : (type->sign ? "h" : "hu"); case 4: return isstore ? "w" : (type->sign ? "w" : "wu"); case 8: return "d"; // isstore ? "d" : (type->sign ? "b" : "bu"); default: if (type->type == CCB_TYPE_STRUCTURE) { ccb_compile_error(ccb, "Unexpected operand to ccb_target_gen_loadstorespec (type STRUCTURE, size=%d) - perhaps you wanted a pointer?", type->size); } else if (type->type == CCB_TYPE_FUNCTION) { ccb_compile_error(ccb, "Unexpected operand to ccb_target_gen_loadstorespec (type FUNCTION, size=%d) - perhaps you wanted a function pointer?", type->size); } else { ccb_compile_error(ccb, "Unexpected operand to ccb_target_gen_loadstorespec (type %d, size=%d)\n", type->type, type->size); } return ""; // Unreachable? } } else { return ""; } } static void ccb_target_gen_load_global(ccb_t* ccb, ccb_data_type_t* type, char* label, int offset) { if (type->type == CCB_TYPE_ARRAY) { if (offset) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, (%s + %d)", label, offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi a0, zero, %s", label); // TODO: Use lui here? ccb_target_gen_emit("la a0, %s", label); //ccb_target_gen_emit("addi a0, a0, %d", offset); ccb_target_gen_rv_addi(ccb, "a0", "a0", offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrcpc r0, %s, %d", label, offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s + %d]", label, offset); } else { ccb_target_gen_emit("lea %s+%d(%%rip), %%rax", label, offset); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, (%s + %d)", label); //ccb_target_gen_emit("read32 $r0, $r1, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %s", label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi a0, zero, %s", label); // TODO: Use lui here? ccb_target_gen_emit("la a0, %s", label); // TODO: Use lui here? } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s]", label); } else { ccb_target_gen_emit("lea %s(%%rip), %%rax", label); } } return; } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r1, $r1, $r1"); ccb_target_gen_emit("addimm $r1, $r1, (%s + %d)", label, offset); if (type->size <= 4) { ccb_target_gen_emit("read32 $r0, $r1, 0"); if (type->size < 4) { int32_t mask = ((type->size == 1) ? 0xFF : 0xFFFF); ccb_target_gen_emit("xor $r1, $r1, $r1"); ccb_target_gen_emit("addimm $r1, %d", mask); // TODO: This might not work out so well for 16-bit, must check. ccb_target_gen_emit("and $r0, $r0, $r1"); } } else { ccb_target_gen_emit("read32 $r0, $r1, 4"); ccb_target_gen_emit("shlz $r0, $r0, 32"); ccb_target_gen_emit("read32 $r1, $r1, 0"); ccb_target_gen_emit("or $r0, $r0, $r1"); // TODO: Sign bit may also interfere here. } } else { if (type->size < 4) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi a0, zero, 0"); // TODO: Probably unneeded? } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov eax, 0"); } else { ccb_target_gen_emit("mov $0, %%eax"); } } const char* reg = ccb_target_gen_register_integer(ccb, type, 'a'); if (offset) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrcpcm %s, %s, %d", reg, label, offset); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %s", label); // TODO: Use lui? Better temporary register? ccb_target_gen_emit("la t0, %s", label); // TODO: Use lui? Better temporary register? //ccb_target_gen_emit("ld %s, t0, %d", reg, offset); //ccb_target_gen_emit("ld %s, %d(t0)", reg, offset); const char* spec = ccb_target_gen_loadstorespec(ccb, type, false); ccb_target_gen_emit("l%s %s, %d(%s)", spec, reg, offset, "t0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, [%s + %d]", reg, label, offset); } else { ccb_target_gen_emit("mov %s+%d(%%rip), %%%s", label, offset, reg); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrcm %s, %s", reg, label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %s", label); // TODO: Use lui? Better temporary register? ccb_target_gen_emit("la t0, %s", label); // TODO: Use lui? Better temporary register? //ccb_target_gen_emit("ld %s, t0, 0", reg); //ccb_target_gen_emit("ld %s, 0(t0)", reg); const char* spec = ccb_target_gen_loadstorespec(ccb, type, false); if (type->type == CCB_TYPE_FLOAT || type->type == CCB_TYPE_DOUBLE || type->type == CCB_TYPE_LDOUBLE) { ccb_target_gen_emit("fl%s %s, %d(%s)", spec, "fa0", 0, "t0"); } else { ccb_target_gen_emit("l%s %s, %d(%s)", spec, reg, 0, "t0"); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, [%s]", reg, label); } else { ccb_target_gen_emit("mov %s(%%rip), %%%s", label, reg); } } } } static void ccb_target_gen_cast_int(ccb_t* ccb, ccb_data_type_t* to, ccb_data_type_t* type) { if (!ccb_ast_type_floating(ccb, type)) { if (type->type == CCB_TYPE_INT) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("db 0x48"); ccb_target_gen_emit("db 0x98"); // TODO: This is test only for now, needs to handle unsigned/other } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86) { ccb_target_gen_emit("cltq"); // TODO: This is test only for now, needs to handle unsigned/other } /* TODO: Fix this elsewhere? May not be an issue on RISC-V since I think it auto-extends (bonus TODO: Check that!) */ if (to != NULL && to->sign && type->sign && type->size == 4) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addw a0, a0, zero"); // Sign extend by using a 32-bit addition } } } return; } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("intf r0, f0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.l.d a0, fa0, rtz"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvttsd2si eax, xmm0"); } else { ccb_target_gen_emit("cvttsd2si %%xmm0, %%eax"); } } static void ccb_target_gen_cast_float(ccb_t* ccb, ccb_data_type_t* type) { if (ccb_ast_type_floating(ccb, type)) { return; } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("floati f0, r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.d.l fa0, a0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtsi2sd xmm0, eax"); } else { ccb_target_gen_emit("cvtsi2sd %%eax, %%xmm0"); } } static int ccb_target_gen_r02nextwd(ccb_t* ccb, ccb_data_type_t* type, int* countvar) { // TODO... if (countvar[0] < type->size) { if (type->size <= ccb_target_wordbytes(ccb)*2) { if (countvar[0] == 0) { // No action, first word is already in r0 } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi %s, %s, 0", ccb_target_r0(ccb), ccb_target_r1(ccb)); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, %s", ccb_target_r0(ccb), ccb_target_r1(ccb)); } else { ccb_target_gen_emit("mov %%%s, %%%s", ccb_target_r1(ccb), ccb_target_r0(ccb)); } } } else { ccb_target_gen_pop(ccb_target_r0(ccb)); } countvar[0] += ccb_target_wordbytes(ccb); return 1; } else { return 0; } } static void ccb_target_gen_load_local(ccb_t* ccb, ccb_data_type_t* var, const char* base, int offset) { if (var->type == CCB_TYPE_ARRAY) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrrpc r0, %s, %d", base, offset); // Set-register-to-register-plus-constant (we only want the address of array variable) } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi a0, %s, %d", base, offset); // Set-register-to-register-plus-constant (we only want the address of array variable) ccb_target_gen_rv_addi(ccb, "a0", base, offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("addimm $r0, %s, %d", base, offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s + %d]", base, offset); } else { ccb_target_gen_emit("lea %d(%%%s), %%rax", offset, base); } } else if (var->type == CCB_TYPE_FLOAT) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setfrpcmx32 f0, %s, %d", base, offset); // Set-float-to-register-plus-constant's-memory-x32bit } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("flw ft0, %d(%s)", offset, base); ccb_target_gen_emit("fcvt.d.s fa0, ft0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("todo"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtps2pd xmm0, [%s + %d]", base, offset); } else { ccb_target_gen_emit("cvtps2pd %d(%%%s), %%xmm0", offset, base); } } else if (var->type == CCB_TYPE_DOUBLE || var->type == CCB_TYPE_LDOUBLE) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setfrpcm f0, %s, %d", base, offset); // Set-float-to-register-plus-constant's-memory } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fld fa0, %d(%s)", offset, base); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("todo"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("movsd xmm0, [%s + %d]", base, offset); } else { ccb_target_gen_emit("movsd %d(%%%s), %%xmm0", offset, base); } } else if (var->size > ccb_target_wordbytes(ccb)) { if (var->size <= ccb_target_wordbytes(ccb)*2) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("ld %s, %d(%s)", ccb_target_r0(ccb), offset, base); ccb_target_gen_emit("ld %s, %d(%s)", ccb_target_r1(ccb), offset+ccb_target_wordbytes(ccb), base); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, [%s + %d]", ccb_target_r0(ccb), base, offset); ccb_target_gen_emit("mov %s, [%s + %d]", ccb_target_r1(ccb), base, offset+ccb_target_wordbytes(ccb)); } else { ccb_target_gen_emit("mov %d(%%%s), %%%s", offset, base, ccb_target_r0(ccb)); ccb_target_gen_emit("mov %d(%%%s), %%%s", offset+ccb_target_wordbytes(ccb), base, ccb_target_r1(ccb)); } } else { /* I could probably hard-code a more efficient way, but we can just read each word from the end to * the start and push each word to the stack as it's read in order to get the correct layout on the * stack. This way the stack accounting doesn't need to be manually adjusted. */ long count = var->size-1; while ((count % ccb_target_wordbytes(ccb)) != 0) { count--; } while (count >= 0) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("ld %s, %d(%s)", ccb_target_r0(ccb), offset+count, base); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, [%s + %d]", ccb_target_r0(ccb), base, offset+count); } else { ccb_target_gen_emit("mov %d(%%%s), %%%s", offset+count, base, ccb_target_r0(ccb)); } ccb_target_gen_push(ccb_target_r0(ccb)); count -= ccb_target_wordbytes(ccb); } //ccb_compile_error(ccb, "TODO: Load large structs..."); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r1, $r1, $r1"); ccb_target_gen_emit("addimm $r1, %s, %d", base, offset); if (var->size <= 4) { ccb_target_gen_emit("read32 $r0, $r1, 0"); if (var->size < 4) { int32_t mask = ((var->size == 1) ? 0xFF : 0xFFFF); ccb_target_gen_emit("xor $r1, $r1, $r1"); ccb_target_gen_emit("addimm $r1, %d", mask); // TODO: This might not work out so well for 16-bit, must check. ccb_target_gen_emit("and $r0, $r0, $r1"); } } else { ccb_target_gen_emit("read32 $r0, $r1, 4"); ccb_target_gen_emit("shlz $r0, $r0, 32"); ccb_target_gen_emit("read32 $r1, $r1, 0"); ccb_target_gen_emit("or $r0, $r0, $r1"); // TODO: Sign bit may also interfere here. } } else { const char* reg = ccb_target_gen_register_integer(ccb, var, 'c'); const char* spec = ccb_target_gen_loadstorespec(ccb, var, false); if (var->size < 4) { // TODO: Should this be 8 (or normal int/pointer size) ?? if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("li a1, 0");// No need to clear top bits, handled in load instruction (??) } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov ecx, 0"); } else { ccb_target_gen_emit("mov $0, %%ecx"); } } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("peek32 %s, %s, %d", reg, base, offset); ccb_target_gen_emit("move v0, v1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrrpcm %s, %s, %d", reg, base, offset); ccb_target_gen_emit("setrr r0, r1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("l%s %s, %d(%s)", spec, reg, offset, base); ccb_target_gen_emit("addi a0, a1, 0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, [%s + %d]", reg, base, offset); if (var->sign && var->size < 8) { //ccb_target_gen_emit("movsx rax, %s", reg); /* Same workaround as for YASM: */ ccb_target_gen_emit("db 0x48"); ccb_target_gen_emit("db 0x63"); ccb_target_gen_emit("db 0xC1"); }/* else if (var->size < 8) { ccb_target_gen_emit("movzx rax, %s", reg); }*/ else { ccb_target_gen_emit("mov rax, %s", "rcx" /*reg*/); } //ccb_target_gen_emit("mov rax, rcx"); } else { ccb_target_gen_emit("mov %d(%%%s), %%%s", offset, base, reg); if (var->sign && var->size == 4) { /* YASM seems to support most/all GNU syntax EXCEPT having a compatible movsxd variant of movsx. * TODO: Check if there's some better way than this. */ ccb_target_gen_emit(".byte 0x48"); ccb_target_gen_emit(".byte 0x63"); ccb_target_gen_emit(".byte 0xC1"); /*if (getenv("USING_YASM") != NULL) { ccb_target_gen_emit("movsxd dword %%%s, %%rax", reg); } else { ccb_target_gen_emit("movsxd %%%s, %%rax", reg); }*/ } else if (var->sign && var->size < 8) { ccb_target_gen_emit("movsx %%%s, %%rax", reg); } /*else if (var->size < 8) { ccb_target_gen_emit("movzx %%%s, %%rax", reg); }*/ else { ccb_target_gen_emit("mov %%%s, %%rax", "rcx"/*reg*/); } //ccb_target_gen_emit("mov %%rcx, %%rax"); } } } } static void ccb_target_gen_rv_addi(ccb_t* ccb, char* destr, char* srcr, int offset) { /*if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrc %s, %d", destr, offset); }*/ if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { /* For RISC-V, the range of an addi instruction is limited to inbetween -2048 and 2047 (inclusive). * The best approach would be to use a scratch register, but since this might be used in critical * places where other register values are important (and because register usage isn't tracked entirely) * a fallback of simply doing multiple additions is used. * * A warning is printed in these cases, so that you'll know if a structure access is outside of the * comfortable range. */ if (offset < -2048) { ccb_compile_warn(ccb, "Hard-coding a long subtraction (addi of %d) for register %s (this generally means a structure or function stack frame is uncomfortably large)", offset, destr); ccb_target_gen_rv_addi(ccb, destr, srcr, -2048); ccb_target_gen_rv_addi(ccb, destr, destr, offset + 2048); } else if (offset > 2047) { ccb_compile_warn(ccb, "Hard-coding a long addition (addi of %d) for register %s (this generally means a structure or function stack frame is uncomfortably large)", offset, destr); ccb_target_gen_rv_addi(ccb, destr, srcr, 2047); ccb_target_gen_rv_addi(ccb, destr, destr, offset - 2047); } else { ccb_target_gen_emit("addi %s, %s, %d", destr, srcr, offset); } } else { ccb_compile_error(ccb, "TODO: Internal error, no long _addi support for non-RISC-V targets"); } /* else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("add %s, %d", destr, offset); } else { ccb_target_gen_emit("add %d, %%%s", offset, destr); }*/ } static void ccb_target_gen_rv_loadstore(ccb_t* ccb, char* op, char* spec, char* r, char* baser, int offset, char* tmpr) { /*if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrc %s, %d", destr, offset); }*/ if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { /* For RISC-V, the range of an addi instruction is limited to inbetween -2048 and 2047 (inclusive). * The best approach would be to use a scratch register, but since this might be used in critical * places where other register values are important (and because register usage isn't tracked entirely) * a fallback of simply doing multiple additions is used. * * A warning is printed in these cases, so that you'll know if a structure access is outside of the * comfortable range. */ if (offset < -2048 || offset > 2047) { ccb_compile_warn(ccb, "Using temporary register %s for long load/store op", tmpr); ccb_target_gen_rv_addi(ccb, tmpr, baser, offset); ccb_target_gen_rv_loadstore(ccb, op, spec, r, tmpr, 0, NULL); } else { ccb_target_gen_emit("%s%s %s, %d(%s)", op, spec, r, offset, baser); } } else { ccb_compile_error(ccb, "TODO: Internal error, no long _addi support for non-RISC-V targets"); } /* else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("add %s, %d", destr, offset); } else { ccb_target_gen_emit("add %d, %%%s", offset, destr); }*/ } static void ccb_target_gen_rv_load(ccb_t* ccb, char* spec, char* r, char* baser, int offset, char* tmpr) { ccb_target_gen_rv_loadstore(ccb, "l", spec, r, baser, offset, tmpr); } static void ccb_target_gen_rv_store(ccb_t* ccb, char* spec, char* r, char* baser, int offset, char* tmpr) { ccb_target_gen_rv_loadstore(ccb, "s", spec, r, baser, offset, tmpr); } static void ccb_target_gen_save_global(ccb_t* ccb, char* name, ccb_data_type_t* type, int offset) { // TODO: Support for floating-point globals, or is this elsewhere? const char* reg = ccb_target_gen_register_integer(ccb, type, 'a'); if (offset) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmr %s, %d, %s", name, offset, reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %s", name); // TODO: lui? ccb_target_gen_emit("la t0, %s", name); const char* spec = ccb_target_gen_loadstorespec(ccb, type, true); ccb_target_gen_emit("s%s %s, %d(%s)", spec, reg, offset, "t0"); //ccb_target_gen_emit("sd %s, %d(t0)", reg, offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [%s + %d], %s", name, offset, reg); } else { ccb_target_gen_emit("mov %%%s, %s+%d(%%rip)", reg, name, offset); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setcmr %s, %s", name, reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %s", name); // TODO: lui? ccb_target_gen_emit("la t0, %s", name); // TODO: lui? const char* spec = ccb_target_gen_loadstorespec(ccb, type, true); if (type->type == CCB_TYPE_FLOAT || type->type == CCB_TYPE_DOUBLE || type->type == CCB_TYPE_LDOUBLE) { ccb_target_gen_emit("fs%s %s, %d(%s)", spec, "fa0", 0, "t0"); } else { ccb_target_gen_emit("s%s %s, %d(%s)", spec, reg, 0, "t0"); //ccb_target_gen_emit("sd %s, 0(t0)", reg); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [%s], %s", name, reg); } else { ccb_target_gen_emit("mov %%%s, %s(%%rip)", reg, name); } } } static void ccb_target_gen_save_inner(ccb_t* ccb, ccb_data_type_t* type, int offset, const char* treg, const char* areg) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpmr %s, %s, %d", treg, areg, offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { const char* spec = ccb_target_gen_loadstorespec(ccb, type, true); //ccb_target_gen_emit("s%s %s, %d(%s)", spec, treg, offset, areg); ccb_target_gen_rv_store(ccb, spec, treg, areg, offset, "t1"); /*if (type->type == CCB_TYPE_INT) { ccb_target_gen_emit("sw %s, %d(%s)", treg, offset, areg); } else { ccb_target_gen_emit("sd %s, %d(%s)", treg, offset, areg); }*/ } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [%s + %d], %s", areg, offset, treg); } else { ccb_target_gen_emit("mov %%%s, %d(%%%s)", treg, offset, areg); } } static int ccb_target_gen_nextwd2r0(ccb_t* ccb, ccb_data_type_t* type, int* countvar) { if (countvar[0] < type->size) { if (type->size <= ccb_target_wordbytes(ccb)*2) { if (countvar[0] == 0) { // No action, first word is already in r0 } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi %s, %s, 0", ccb_target_r0(ccb), ccb_target_r1(ccb)); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, %s", ccb_target_r0(ccb), ccb_target_r1(ccb)); } else { ccb_target_gen_emit("mov %%%s, %%%s", ccb_target_r1(ccb), ccb_target_r0(ccb)); } } } else { ccb_target_gen_pop(ccb_target_r0(ccb)); } countvar[0] += ccb_target_wordbytes(ccb); // TODO: Update this to return number of bytes to write instead? return 1; } else { return 0; } } static void ccb_target_gen_save_dolongstore(ccb_t* ccb, ccb_data_type_t* type, const char* base, int offset, int strict) { // TODO: This will overwrite some bytes past the end of non-word-aligned struct sizes, this probably needs to be fixed // for some cases (i.e. if it's within another struct) but is less important for local/global variables and parameters // (which are mostly treated as locals) since those should all be word-aligned anyway. If I was pedantic I'd check // whether it's faster or slower to read a whole word versus 1-7 bytes, and my guess is that it'd depend on the // number of bytes and how many instructions it would otherwise take to read/write them, but reading/writing a whole word // is simpler in cases where it does work. int countvar = 0; while (ccb_target_gen_nextwd2r0(ccb, type, &countvar)) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmr %s, %d, %s", base, offset, ccb_target_gen_register_integer(ccb, type, 'a')); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("sd %s, %d(fp)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); ccb_target_gen_emit("sd %s, %d(%s)", ccb_target_r0(ccb), offset+countvar-ccb_target_wordbytes(ccb), base); // TODO: Work out why it didn't work this way... //ccb_target_gen_save_inner(ccb, ccb_ast_data_table[CCB_TYPE_LONG], offset+countvar-ccb_target_wordbytes(ccb), ccb_target_r0(ccb), "x8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [%s + %d], %s", base, offset+countvar-ccb_target_wordbytes(ccb), ccb_target_r0(ccb)); } else { ccb_target_gen_emit("mov %%%s, %d(%%%s)", ccb_target_r0(ccb), offset+countvar-ccb_target_wordbytes(ccb), base); } } } static void ccb_target_gen_save_local(ccb_t* ccb, ccb_data_type_t* type, int offset) { if (type->type == CCB_TYPE_FLOAT) { ccb_target_gen_push_xmm(0); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmfx32 r5, %d, f0", offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.s.d ft0, fa0"); //ccb_target_gen_emit("fsw ft0, %d(fp)", offset); ccb_target_gen_emit("fsw ft0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("unpcklpd xmm0, xmm0"); ccb_target_gen_emit("cvtpd2ps xmm0, xmm0"); ccb_target_gen_emit("movss [rbp + %d], xmm0", offset); } else { ccb_target_gen_emit("unpcklpd %%xmm0, %%xmm0"); ccb_target_gen_emit("cvtpd2ps %%xmm0, %%xmm0"); ccb_target_gen_emit("movss %%xmm0, %d(%%rbp)", offset); } ccb_target_gen_pop_xmm(0); } else if (type->type == CCB_TYPE_DOUBLE || type->type == CCB_TYPE_LDOUBLE) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmf r5, %d, f0", offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("fsd fa0, %d(fp)", offset); ccb_target_gen_emit("fsd fa0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("movsd [rbp + %d], xmm0", offset); } else { ccb_target_gen_emit("movsd %%xmm0, %d(%%rbp)", offset); } } /*else if (type->type == CCB_TYPE_INT) { // TODO: This needs to be cleaned up! if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmr r5, %d, %s", offset, ccb_target_gen_register_integer(ccb, type, 'a')); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("sd %s, %d(fp)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); ccb_target_gen_emit("sw %s, %d(x8)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [rbp + %d], %s", offset, ccb_target_gen_register_integer(ccb, type, 'a')); } else { ccb_target_gen_emit("mov %%%s, %d(%%rbp)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); } }*/ else if (type->size > ccb_target_wordbytes(ccb)) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_save_dolongstore(ccb, type, "x8", offset, 0); } else { ccb_target_gen_save_dolongstore(ccb, type, "rbp", offset, 0); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmr r5, %d, %s", offset, ccb_target_gen_register_integer(ccb, type, 'a')); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("sd %s, %d(fp)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); //ccb_target_gen_emit("sd %s, %d(x8)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); ccb_target_gen_save_inner(ccb, type, offset, ccb_target_gen_register_integer(ccb, type, 'a'), "x8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [rbp + %d], %s", offset, ccb_target_gen_register_integer(ccb, type, 'a')); } else { ccb_target_gen_emit("mov %%%s, %d(%%rbp)", ccb_target_gen_register_integer(ccb, type, 'a'), offset); } } } static void ccb_target_gen_assignment_dereference_intermediate(ccb_t* ccb, ccb_data_type_t* type, int offset) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrrm r1, r4"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("ld a1, 0(sp)"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rcx, [rsp]"); } else { ccb_target_gen_emit("mov (%%rsp), %%rcx"); } const char* reg = ccb_target_gen_register_integer(ccb, type, 'c'); if (offset) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmr r0, %d, %s", offset, reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { // TODO: This part (and again below) needs to be cleaned up... /*if (type->type == CCB_TYPE_INT) { ccb_target_gen_emit("sw %s, %d(a0)", reg, offset); } else { ccb_target_gen_emit("sd %s, %d(a0)", reg, offset); }*/ ccb_target_gen_save_inner(ccb, type, offset, reg, ccb_target_gen_register_integer(ccb, type, 'a')); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [rax + %d], %s", offset, reg); } else { ccb_target_gen_emit("mov %%%s, %d(%%rax)", reg, offset); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrmr r0, %s", reg); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { /*if (type->type == CCB_TYPE_INT) { ccb_target_gen_emit("sw %s, 0(a0)", reg); } else { ccb_target_gen_emit("sd %s, 0(a0)", reg); }*/ ccb_target_gen_save_inner(ccb, type, 0, reg, ccb_target_gen_register_integer(ccb, type, 'a')); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov [rax], %s", reg); } else { ccb_target_gen_emit("mov %%%s, (%%rax)", reg); } } ccb_target_gen_pop(ccb_target_r0(ccb)); } static void ccb_target_gen_assignment_dereference(ccb_t* ccb, ccb_ast_t* var) { ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, var->unary.operand); ccb_target_gen_assignment_dereference_intermediate(ccb, var->unary.operand->ctype->pointer, 0); } static void ccb_target_gen_ensure_lva(ccb_t* ccb, ccb_ast_t* ast) { if (ast->variable.init) ccb_target_gen_declaration_initialization(ccb, ast->variable.init, ast->variable.off); ast->variable.init = NULL; } static void ccb_target_gen_pointer_arithmetic(ccb_t* ccb, char op, ccb_ast_t* left, ccb_ast_t* right) { ccb_target_gen_expression(ccb, left); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, right); int size = left->ctype->pointer->size; if (size > 1) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("mulrc r0, %d", size); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (size == 2 || size == 4 || size == 8) { ccb_target_gen_emit("slli a0, a0, %d", (size == 2) ? 1 : ((size == 4) ? 2 : 3)); } else { //ccb_target_gen_emit("addi a1, zero, %d", size); ccb_target_gen_emit("li a1, %d", size); ccb_target_gen_emit("mul a0, a0, a1"); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("imul rax, %d", size); } else { ccb_target_gen_emit("imul $%d, %%rax", size); } } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r1, r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi a1, a0, 0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rcx, rax"); } else { ccb_target_gen_emit("mov %%rax, %%rcx"); } ccb_target_gen_pop(ccb_target_r0(ccb)); if (op == '-') { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("subrr r0, r1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("sub a0, a0, a1"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("sub rax, rcx"); } else { ccb_target_gen_emit("sub %%rcx, %%rax"); } } else if (op == '+') { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrr r0, r1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("add a0, a0, a1"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("add rax, rcx"); } else { ccb_target_gen_emit("add %%rcx, %%rax"); } } else { ccb_compile_error(ccb, "Unhandled pointer op '%c'\n", op); } } static void ccb_target_gen_assignment_structure(ccb_t* ccb, ccb_ast_t* structure, ccb_data_type_t* field, int offset) { switch (structure->type) { case CCB_AST_TYPE_VAR_LOCAL: ccb_target_gen_ensure_lva(ccb, structure); ccb_target_gen_save_local(ccb, field, structure->variable.off + field->offset + offset); break; case CCB_AST_TYPE_VAR_GLOBAL: ccb_target_gen_save_global(ccb, structure->variable.label, field, field->offset + offset); break; case CCB_AST_TYPE_STRUCT: ccb_target_gen_assignment_structure(ccb, structure->structure, field, offset + structure->ctype->offset); break; case CCB_AST_TYPE_DEREFERENCE: ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, structure->unary.operand); ccb_target_gen_assignment_dereference_intermediate(ccb, field, field->offset + offset); break; default: ccb_compile_error(ccb, "Internal error: gen_assignment_structure"); break; } } static void ccb_target_gen_load_structure(ccb_t* ccb, ccb_ast_t* structure, ccb_data_type_t* field, int offset, int pointeronly) { switch (structure->type) { case CCB_AST_TYPE_VAR_LOCAL: ccb_target_gen_ensure_lva(ccb, structure); ccb_target_gen_load_local(ccb, field, ccb_target_bp(ccb), structure->variable.off + field->offset + offset); break; case CCB_AST_TYPE_VAR_GLOBAL: ccb_target_gen_load_global(ccb, field, structure->variable.label, field->offset + offset); break; case CCB_AST_TYPE_STRUCT: ccb_target_gen_load_structure(ccb, structure->structure, field, structure->ctype->offset + offset, pointeronly); break; case CCB_AST_TYPE_DEREFERENCE: ccb_target_gen_expression(ccb, structure->unary.operand); if (pointeronly) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrc %s, %d", ccb_target_r0(ccb), field->offset + offset); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi %s, %s, %d", ccb_target_r0(ccb), ccb_target_r0(ccb), field->offset + offset); ccb_target_gen_rv_addi(ccb, ccb_target_r0(ccb), ccb_target_r0(ccb), field->offset + offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("add %s, %d", ccb_target_r0(ccb), field->offset + offset); } else { ccb_target_gen_emit("add %d, %%%s", field->offset + offset, ccb_target_r0(ccb)); } } else { ccb_target_gen_load_local(ccb, field, ccb_target_r0(ccb), field->offset + offset); } break; default: ccb_compile_error(ccb, "Internal error: gen_assignment_structure"); break; } } static void ccb_target_gen_assignment(ccb_t* ccb, ccb_ast_t* var) { switch (var->type) { case CCB_AST_TYPE_DEREFERENCE: ccb_target_gen_assignment_dereference(ccb, var); break; case CCB_AST_TYPE_STRUCT: ccb_target_gen_assignment_structure(ccb, var->structure, var->ctype, 0); break; case CCB_AST_TYPE_VAR_LOCAL: ccb_target_gen_ensure_lva(ccb, var); ccb_target_gen_save_local(ccb, var->ctype, var->variable.off); break; case CCB_AST_TYPE_VAR_GLOBAL: ccb_target_gen_save_global(ccb, var->variable.label, var->ctype, 0); break; default: ccb_compile_error(ccb, "Internal error: gen_assignment"); } } static void ccb_target_gen_comparision(ccb_t* ccb, char* operation, ccb_ast_t* ast) { if (ccb_ast_type_floating(ccb, ast->left->ctype) || ccb_ast_type_floating(ccb, ast->right->ctype)) { ccb_target_gen_expression(ccb, ast->left); ccb_target_gen_cast_float(ccb, ast->left->ctype); ccb_target_gen_push_xmm(0); ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_cast_float(ccb, ast->right->ctype); ccb_target_gen_pop_xmm(1); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("compff f1, f0 ; TODO: Right way around?"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_strcasecmp(operation, "setl") == 0) { ccb_target_gen_emit("flt.d a0, fa1, fa0"); } else if (ccb_strcasecmp(operation, "setg") == 0) { ccb_target_gen_emit("flt.d a0, fa0, fa1"); } else if (ccb_strcasecmp(operation, "setle") == 0) { ccb_target_gen_emit("fle.d a0, fa1, fa0"); } else if (ccb_strcasecmp(operation, "setge") == 0) { ccb_target_gen_emit("fle.d a0, fa0, fa1"); } else if (ccb_strcasecmp(operation, "sete") == 0) { ccb_target_gen_emit("feq.d a0, fa0, fa1"); } else if (ccb_strcasecmp(operation, "setne") == 0) { ccb_target_gen_emit("feq.d a0, fa0, fa1"); ccb_target_gen_emit("xori a0, a0, 1"); } else { ccb_compile_error(ccb, "Bad internal comparison operator"); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("ucomisd xmm1, xmm0"); } else { ccb_target_gen_emit("ucomisd %%xmm0, %%xmm1"); } } else { ccb_target_gen_expression(ccb, ast->left); ccb_target_gen_cast_int(ccb, NULL, ast->left->ctype); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_cast_int(ccb, NULL, ast->right->ctype); ccb_target_gen_pop(ccb_target_r1(ccb)); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprr r1, r0 ; TODO: Right way around?"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_strcasecmp(operation, "setl") == 0) { ccb_target_gen_emit("slt a0, a1, a0"); } else if (ccb_strcasecmp(operation, "setg") == 0) { ccb_target_gen_emit("slt a0, a0, a1"); } else if (ccb_strcasecmp(operation, "setle") == 0) { ccb_target_gen_emit("slt a0, a0, a1"); ccb_target_gen_emit("xori a0, a0, 1"); // XXX TODO PRObLEMATIC... } else if (ccb_strcasecmp(operation, "setge") == 0) { ccb_target_gen_emit("slt a0, a1, a0"); ccb_target_gen_emit("xori a0, a0, 1"); } else if (ccb_strcasecmp(operation, "sete") == 0) { ccb_target_gen_emit("xor a0, a0, a1"); ccb_target_gen_emit("seqz a0, a0"); } else if (ccb_strcasecmp(operation, "setne") == 0) { ccb_target_gen_emit("xor a0, a0, a1"); ccb_target_gen_emit("snez a0, a0"); } else { ccb_compile_error(ccb, "Bad internal comparison operator"); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cmp rcx, rax ; TODO: Right way around?"); } else { ccb_target_gen_emit("cmp %%rax, %%rcx"); } } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%s r0", operation); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { // No cleanup needed } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s al", operation); ccb_target_gen_emit("movzx eax, al ; TODO: Right instruction?"); } else { ccb_target_gen_emit("%s %%al", operation); ccb_target_gen_emit("movzx %%al, %%eax"); //TODO: Check if movzx & movzb are the same: ccb_target_gen_emit("movzb %%al, %%eax"); } } static void ccb_target_gen_binary_arithmetic_integer(ccb_t* ccb, ccb_ast_t* ast) { char* op; switch (ast->type) { case '+': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "addrr" : "add"); break; case '-': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "subrr" : "sub"); break; case '*': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "mulrr" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? "mul" : "imul")); break; case '^': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "xorrr" : "xor"); break; case CCB_AST_TYPE_LSHIFT: op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "shlrr" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? /*"sla"*/ "sll" : "sal")); break; case CCB_AST_TYPE_RSHIFT: op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "shrrr" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? (ast->ctype->sign ? "sra" : "srl") :"sar")); break; // TODO: Unsigned variant? case '/': case '%': op = NULL; // Avoid uninitialised warning/error break; default: ccb_compile_error(ccb, "Internal error: gen_binary"); return; // Seems intended. -Zak break; } ccb_target_gen_expression(ccb, ast->left); ccb_target_gen_cast_int(ccb, NULL, ast->left->ctype); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_cast_int(ccb, NULL, ast->right->ctype); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r1, r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("addimm $r1, $r0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi a1, a0, 0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rcx, rax"); } else { ccb_target_gen_emit("mov %%rax, %%rcx"); } ccb_target_gen_pop(ccb_target_r0(ccb)); if (ast->type == '/' || ast->type == '%') { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("signx64x32 r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("sext.w a0, a0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cqo"); } else { ccb_target_gen_emit("cqto"); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("div r0, r2, r1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("%s a0, a0, a1", ast->type == '%' ? "rem" : "div"); //if (ast->type != '%'){ ccb_target_gen_emit("sext.w a0, a0"); //} } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("idiv rcx"); } else { ccb_target_gen_emit("idiv %%rcx"); } if (ast->type == '%') { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r0, r2"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { // Handled above } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov eax, edx"); } else { ccb_target_gen_emit("mov %%edx, %%eax"); } } } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ast->type == CCB_AST_TYPE_LSHIFT || ast->type == CCB_AST_TYPE_RSHIFT)) { if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s rax, cl", op); } else { ccb_target_gen_emit("%s %%cl, %%rax", op); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%s r0, r1", op); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("%s $r0, $r0, $r1", op); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("%s a0, a0, a1", op); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s rax, rcx", op); } else { ccb_target_gen_emit("%s %%rcx, %%rax", op); } } } static void ccb_target_gen_binary_arithmetic_floating(ccb_t* ccb, ccb_ast_t* ast) { char* op; switch (ast->type) { case '+': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "addff" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? "fadd.d" : "addsd")); break; case '-': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "subff" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? "fsub.d" : "subsd")); break; case '*': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "mulff" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? "fmul.d" : "mulsd")); break; case '/': op = (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "divff" : (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV ? "fdiv.d" : "divsd")); break; default: ccb_compile_error(ccb, "Internal error: gen_binary"); return; // XXX Seems intended. -Zak break; } ccb_target_gen_expression(ccb, ast->left); ccb_target_gen_cast_float(ccb, ast->left->ctype); ccb_target_gen_push_xmm(0); ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_cast_float(ccb, ast->right->ctype); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("movff fa1, fa0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fmv.d fa1, fa0"); // NOTE: It's very easy to get f0/f1 and fa0/fa1 mixed up - this previously prevented arithmetic from working! } else { ccb_target_gen_emit("movsd %%xmm0, %%xmm1"); // TODO: Is this wrong order for FASM? } ccb_target_gen_pop_xmm(0); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%s f0, f1", op); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("%s fa0, fa0, fa1", op); } else { ccb_target_gen_emit("%s %%xmm1, %%xmm0", op); // TODO: Is this wrong order for FASM? (Or is it corrected by other reversals?) } } static void ccb_target_gen_load(ccb_t* ccb, ccb_data_type_t* to, ccb_data_type_t* from) { if (ccb_ast_type_floating(ccb, to)) ccb_target_gen_cast_float(ccb, from); else ccb_target_gen_cast_int(ccb, to, from); } static void ccb_target_gen_save(ccb_t* ccb, ccb_data_type_t* to, ccb_data_type_t* from) { if (ccb_ast_type_integer(ccb, from) && to->type == CCB_TYPE_FLOAT) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("floatrx32 f0, r0"); // Float-of-register-x32bit } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.s.i fa0, a0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtsi2ss xmm0, eax"); } else { ccb_target_gen_emit("cvtsi2ss %%eax, %%xmm0"); } } else if (ccb_ast_type_floating(ccb, from) && to->type == CCB_TYPE_FLOAT) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("floatsfd f0, f0"); // Float-single-of-float-double } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.s.d fa0, fa0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtpd2ps xmm0, xmm0"); } else { ccb_target_gen_emit("cvtpd2ps %%xmm0, %%xmm0"); } } else if (ccb_ast_type_integer(ccb, from) && (to->type == CCB_TYPE_DOUBLE || to->type == CCB_TYPE_LDOUBLE)) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("floatr f0, r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.d.l fa0, a0"); // NOTE: .i on 32-bit? } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtsi2sd xmm0, eax"); } else { ccb_target_gen_emit("cvtsi2sd %%eax, %%xmm0"); } } else if (!(ccb_ast_type_floating(ccb, from) && (to->type == CCB_TYPE_DOUBLE || to->type == CCB_TYPE_LDOUBLE))) { ccb_target_gen_load(ccb, to, from); // TODO: I'm hoping this is a reasonable code-path as long as to/from ordering is known? } } static void ccb_target_gen_binary(ccb_t* ccb, ccb_ast_t* ast) { if (ast->ctype->type == CCB_TYPE_POINTER) { ccb_target_gen_pointer_arithmetic(ccb, ast->type, ast->left, ast->right); return; } switch (ast->type) { // TODO: Unsigned variants case '<': ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflaglt" : "setl"), ast); return; case '>': ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflaggt" : "setg"), ast); return; case CCB_AST_TYPE_EQUAL: ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflaget" : "sete"), ast); return; case CCB_AST_TYPE_GEQUAL: ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflagge" : "setge"), ast); return; case CCB_AST_TYPE_LEQUAL: ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflagle" : "setle"), ast); return; case CCB_AST_TYPE_NEQUAL: ccb_target_gen_comparision(ccb, (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC ? "setrflagne" : "setne"), ast); return; } if (ccb_ast_type_integer(ccb, ast->ctype)) ccb_target_gen_binary_arithmetic_integer(ccb, ast); else if (ccb_ast_type_floating(ccb, ast->ctype)) ccb_target_gen_binary_arithmetic_floating(ccb, ast); else ccb_compile_error(ccb, "Internal error in ccb_target_gen_binary, type=%d", ast->ctype->type); } static void ccb_target_gen_literal_save(ccb_t* ccb, ccb_ast_t* ast, ccb_data_type_t* type, int offset) { switch (type->type) { case CCB_TYPE_CHAR: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmcx8 r5, %d, %d", offset, ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %d", ast->integer); ccb_target_gen_emit("li t0, %d", ast->integer); //ccb_target_gen_emit("sb t0, %d(fp)", offset); ccb_target_gen_emit("sb t0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov byte [rbp + %d], %d", offset, ast->integer); } else { ccb_target_gen_emit("movb $%d, %d(%%rbp)", ast->integer, offset); } break; case CCB_TYPE_SHORT: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmcx16 r5, %d, %d", offset, ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %d", ast->integer); ccb_target_gen_emit("li t0, %d", ast->integer); //ccb_target_gen_emit("sh t0, %d(fp)", offset); ccb_target_gen_emit("sh t0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov word [rbp + %d], %d", offset, ast->integer); } else { ccb_target_gen_emit("movw $%d, %d(%%rbp)", ast->integer, offset); } break; case CCB_TYPE_INT: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrpcmcx32 r5, %d, %d", offset, ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, %d", ast->integer); ccb_target_gen_emit("li t0, %d", ast->integer); //ccb_target_gen_emit("sw t0, %d(fp)", offset); ccb_target_gen_emit("sw t0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov dword [rbp + %d], %d", offset, ast->integer); } else { ccb_target_gen_emit("movl $%d, %d(%%rbp)", ast->integer, offset); } break; case CCB_TYPE_LONG: case CCB_TYPE_LLONG: case CCB_TYPE_POINTER: ccb_target_gen_push(ccb_target_r0(ccb)); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("setrc r0, %llu", tmp /*(unsigned long long)ast->integer*/); ccb_target_gen_emit("setrpcmr r5, %d, r0", offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ //ccb_target_gen_emit("addi t0, zero, %d", tmp); // TODO: Handle larger literals! ccb_target_gen_emit("li t0, %d", tmp); // TODO: Handle larger literals! //ccb_target_gen_emit("sd t0, %d(fp)", offset); ccb_target_gen_emit("sd t0, %d(x8)", offset); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("mov qword rax, %llu", tmp); ccb_target_gen_emit("mov qword [rbp + %d], rax", offset); } else { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("movq $%llu, %%rax", tmp); ccb_target_gen_emit("movq %%rax, %d(%%rbp)", offset); } ccb_target_gen_pop(ccb_target_r0(ccb)); break; case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: ccb_target_gen_push(ccb_target_r0(ccb)); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { double tmp = ast->floating.value; // TODO: Allow getting address of struct member ccb_target_gen_emit("setrc rax, %llu", *((unsigned long long*) &tmp)); ccb_target_gen_emit("setrpcmr r5, %d, r0", offset); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { double tmp = ast->floating.value; // TODO: Allow getting address of struct member ccb_target_gen_emit("li a0, %llu", *((unsigned long long*) & tmp)); ccb_target_gen_emit("sd a0,%d(x8)", offset); //ccb_target_gen_emit("fmv.d.x fa0, a0"); //ccb_target_gen_emit("lui a0,%%hi(%s)", ast->floating.label); //ccb_target_gen_emit("fld fa0,%%lo(%s)(a0)", ast->floating.label); //ccb_target_gen_emit("fsd fa0,%d(x8)", offset); /* lui a5,%hi(.LC0) fld fa5,%lo(.LC0)(a5) fsd fa5,-24(s0) */ } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { double tmp = ast->floating.value; // TODO: Allow getting address of struct member ccb_target_gen_emit("mov rax, %llu", *((unsigned long long*) & tmp)); ccb_target_gen_emit("mov [rbp + %d], rax", offset); } else { double tmp = ast->floating.value; // TODO: Allow getting address of struct member ccb_target_gen_emit("movq $%llu, %%rax", *((unsigned long long*) & tmp)); ccb_target_gen_emit("movq %%rax, %d(%%rbp)", offset); } ccb_target_gen_pop(ccb_target_r0(ccb)); break; default: ccb_compile_error(ccb, "codegen internal error in %s", __func__); } } static void ccb_target_gen_declaration_initialization(ccb_t* ccb, ccb_list_t* init, int offset) { for (ccb_list_iterator_t* it = ccb_list_iterator(init); !ccb_list_iterator_end(it); ) { ccb_ast_t* node = ccb_list_iterator_next(it); if (node->init.value->type == CCB_AST_TYPE_LITERAL) ccb_target_gen_literal_save(ccb, node->init.value, node->init.type, node->init.offset + offset); else { ccb_target_gen_expression(ccb, node->init.value); ccb_target_gen_save_local(ccb, node->init.type, node->init.offset + offset); } } } /* Production of prefix/postfix ++ and -- operators requires a little care to find the correct size of the increment. * If the target type is a pointer, then it should be incremented/decremented by the element size. */ static int ccb_target_incrsize(ccb_t* ccb, ccb_ast_t* ast) { switch (ast->ctype->type) { case CCB_TYPE_ARRAY: // TODO: Should this be here? case CCB_TYPE_POINTER: return ast->ctype->pointer->size; default: return 1; } } static void ccb_target_gen_emit_prefix(ccb_t* ccb, ccb_ast_t* ast, const char* op) { // Note: The "right-hand-side" is put in "left". This might be changed later. //fprintf(stderr, "NOTE: prefix left is type %d, incrsize is %d\n", ast->left->type, ccb_target_incrsize(ccb, ast->left)); int size = ccb_target_incrsize(ccb, ast->left); //1; //, ast->right->ctype->size ccb_target_gen_expression(ccb, ast->unary.operand); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%src r0, %d", op, size); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_strcasecmp(op, "sub") == 0) { //ccb_target_gen_emit("addi a0, a0, -%d", size); ccb_target_gen_rv_addi(ccb, "a0", "a0", -size); } else { //ccb_target_gen_emit("addi a0, a0, %d", size); ccb_target_gen_rv_addi(ccb, "a0", "a0", size); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s rax, %d", op, size); } else { ccb_target_gen_emit("%s $%d, %%rax", op, size); } ccb_target_gen_assignment(ccb, ast->unary.operand); } static void ccb_target_gen_emit_postfix(ccb_t* ccb, ccb_ast_t* ast, const char* op) { //fprintf(stderr, "NOTE: postfix left is type %d, incrsize is %d\n", ast->left->type, ccb_target_incrsize(ccb, ast->left)); int size = ccb_target_incrsize(ccb, ast->left); ccb_target_gen_expression(ccb, ast->unary.operand); ccb_target_gen_push(ccb_target_r0(ccb)); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%src r0, %d", op, size); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { if (ccb_strcasecmp(op, "sub") == 0) { //ccb_target_gen_emit("addi a0, a0, -%d", size); ccb_target_gen_rv_addi(ccb, "a0", "a0", -size); } else { //ccb_target_gen_emit("addi a0, a0, %d", size); ccb_target_gen_rv_addi(ccb, "a0", "a0", size); } } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s rax, %d", op, size); } else { ccb_target_gen_emit("%s $%d, %%rax", op, size); } ccb_target_gen_assignment(ccb, ast->unary.operand); ccb_target_gen_pop(ccb_target_r0(ccb)); } static ccb_list_t* ccb_target_gen_function_argument_types(ccb_t* ccb, ccb_ast_t* ast) { ccb_list_t* list = ccb_list_create(); ccb_list_iterator_t* jt = ccb_list_iterator(ast->function.call.paramtypes); // TODO: Compiler doesn't support multiple initialisers in for statements properly! for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.call.args); !ccb_list_iterator_end(it); ) { //fprintf(stderr, "Got list iterators @%lx, %lx\n", it, jt); ccb_ast_t* value = ccb_list_iterator_next(it); //fprintf(stderr, "Got value @%lx\n", value); ccb_data_type_t* type = ccb_list_iterator_next(jt); //fprintf(stderr, "Got type @%lx\n", type); ccb_list_push(list, type ? type : ccb_ast_result_type(ccb, '=', value->ctype, ccb_ast_data_table[CCB_AST_DATA_INT])); } return list; } static void ccb_target_gen_je(ccb_t* ccb, const char* label) { // TODO: This should be called ..gen_jz (jump-if-zero) if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("jumpcifeq %s", label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi t0, zero, 0"); ccb_target_gen_emit("beq a0, zero, %s", label); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("test rax, rax"); ccb_target_gen_emit("je %s", label); } else { ccb_target_gen_emit("test %%rax, %%rax"); ccb_target_gen_emit("je %s", label); } } static void ccb_target_gen_label_impl(ccb_t* ccb, int ln, const char* label) { ccb_target_gen_emit("%s: %s from ln%d", label, (ccb_target_asmfmt(ccb)==CCB_TARGET_ASMFMT_FASM||ccb_target_asmfmt(ccb)==CCB_TARGET_ASMFMT_NASM)?";":"#", ln); } // TODO/FIXME: This breaks some self-tests at present //#define ccb_target_gen_label(ccb,lbl) ccb_target_gen_label_impl(ccb,__LINE__,lbl) #define ccb_target_gen_label(ccb,lbl) ccb_target_gen_label_impl(ccb,0,lbl) static void ccb_target_gen_jmp_impl(ccb_t* ccb, int line, const char* label) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("jumpc %s", label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("j %s", label); } else { ccb_target_gen_emit("jmp %s %s from ln%d", label, (ccb_target_asmfmt(ccb)==CCB_TARGET_ASMFMT_FASM||ccb_target_asmfmt(ccb)==CCB_TARGET_ASMFMT_NASM)?";":"#", line); } } // TODO/FIXME: This breaks some self-tests at present //#define ccb_target_gen_jmp(ccb,lbl) ccb_target_gen_jmp_impl(ccb,__LINE__,lbl) #define ccb_target_gen_jmp(ccb,lbl) ccb_target_gen_jmp_impl(ccb,0,lbl) int tmpintctr;// TODO: Fails to self-compile: = 999; static long ccb_target_wordaligned(ccb_t* ccb, long s) { while ((s%ccb_target_wordbytes(ccb)) != 0) { s++; } return s; } static void ccb_target_gen_statement(ccb_t* ccb, ccb_ast_t* ast) { if (!ast) return; ccb_target_gen_expression(ccb, ast); switch (ast->type) { case CCB_AST_TYPE_STATEMENT_RETURN: return; default: break; } if (ast->ctype == NULL) { return; } else if (ast->ctype->size > ccb_target_wordbytes(ccb)*2) { ccb_compile_warn(ccb, "Popping result..."); long s = ast->ctype->size; while (s > 0) { //TODO... ccb_target_gen_drop(); s -= ccb_target_wordbytes(ccb); } } } static void ccb_target_gen_expression(ccb_t* ccb, ccb_ast_t* ast) { //printf("Processing expression type 0x100+%d\n", ast == NULL ? -1 : ast->type-0x100); if (!ast) return; char* begin = NULL; char* ne = NULL; char* end = NULL; char* step = NULL; char* skip = NULL; int regi = 0, backi; int regx = 0, backx; int nstackints = 0, istackints; int nstackfloats = 0, istackfloats; int nregints = 0, iregints; int nregfloats = 0, iregfloats; ccb_list_t* argtypes; int callconv = 0; int largeresult = 0; switch (ast->type) { case CCB_AST_TYPE_LITERAL: switch (ast->ctype->type) { case CCB_TYPE_CHAR: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %d", ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, $r0, %d", ast->integer); // TODO: Sizing } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi a0, zero, %d", ast->integer); // TODO: Sizing } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rax, %d", ast->integer); } else { ccb_target_gen_emit("mov $%d, %%rax", ast->integer); } break; case CCB_TYPE_INT: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %d", ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, $r0, %d", ast->integer); // TODO: Sizing } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi a0, zero, %d", ast->integer); // TODO: Sizing //ccb_target_gen_emit("lui a0, %%hi(%d)", ast->integer); // TODO: Sizing //ccb_target_gen_emit("addi a0, a0, %%lo(%d)", ast->integer); // TODO: Sizing ccb_target_gen_emit("li a0, %d", ast->integer); // TODO: Sizing } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("ldr x0, =%d", ast->integer); // TODO: Sizing } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rax, %d", ast->integer); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { tmpintctr++; ccb_target_gen_emit("section data"); ccb_target_gen_emit("tmpint%d: data64 %d", tmpintctr, ast->integer); ccb_target_gen_emit("section code"); ccb_target_gen_emit("data8 0x48, 0x89, 0x04, 0x25 ; mov rax, qword ds:..."); ccb_target_gen_emit("data32 tmpint%d", tmpintctr); } else { ccb_target_gen_emit("mov $%d, %%rax", ast->integer); } break; case CCB_TYPE_LONG: case CCB_TYPE_LLONG: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("setrc r0, %llu", tmp); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, $r0, %llu", tmp); // TODO: Sizing } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { unsigned long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("li a0, 0x%llx", tmp); // TODO: Sizing /*ccb_target_gen_emit("li t0, 0x%x", (tmp>>32)&0xFFFFFFFFL); // TODO: Sizing ccb_target_gen_emit("slli t0, t0, 32"); // TODO: Sizing ccb_target_gen_emit("li a0, 0x%x", tmp&0xFFFFFFFFL); // TODO: Sizing ccb_target_gen_emit("slli a0, a0, 32"); // TODO: Sizing ccb_target_gen_emit("srli a0, a0, 32"); // TODO: Sizing ccb_target_gen_emit("or a0, a0, t0"); // TODO: Sizing*/ } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("mov rax, %lld", tmp); } else { long long tmp = ast->integer; /* TODO: Casts in function calls? */ ccb_target_gen_emit("mov $%lld, %%rax", tmp); } break; case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setfcm f0, %s", ast->floating.label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("todo"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("la t0, %s", ast->floating.label); //ccb_target_gen_emit("lui t0, %%hi(%s)", ast->floating.label); //ccb_target_gen_emit("addi t0, t0, %%lo(%s)", ast->floating.label); ccb_target_gen_emit("fld fa0, 0(t0)"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("movsd xmm0, [%s]", ast->floating.label); } else { ccb_target_gen_emit("movsd %s(%%rip), %%xmm0", ast->floating.label); } break; default: ccb_compile_error(ccb, "Internal error: gen_expression"); } break; case CCB_AST_TYPE_STRING: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %s", ast->string.label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("todo"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("lui a0, %%hi(%s)", ast->string.label); //ccb_target_gen_emit("addi a0, a0, %%lo(%s)", ast->string.label); ccb_target_gen_emit("la a0, %s", ast->string.label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("ldr x0, =%s", ast->string.label); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s] ; TODO: Offset from RIP explicitly?", ast->string.label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0x48, 0x8D, 0x04, 0x25 ; lea rax, [...]"); ccb_target_gen_emit("data32 %s ; Pointer to string", ast->string.label); } else { ccb_target_gen_emit("lea %s(%%rip), %%rax", ast->string.label); } break; case CCB_AST_TYPE_VAR_LOCAL: ccb_target_gen_ensure_lva(ccb, ast); ccb_target_gen_load_local(ccb, ast->ctype, ccb_target_bp(ccb), ast->variable.off); break; case CCB_AST_TYPE_VAR_GLOBAL: ccb_target_gen_load_global(ccb, ast->ctype, ast->variable.label, 0); break; case CCB_AST_TYPE_PTRCALL: // TODO: Test this again... ccb_target_gen_push(ccb_target_r15(ccb)); //ccb_target_gen_expression(ccb, ast->function.call.callable); // was ccb_target_gen_push("$rF"); //ccb_target_gen_push(ccb_target_r0(ccb)); //ccb_target_gen_pop(ccb_target_r15(ccb)); //was ccb_target_gen_pop("$rF"); case CCB_AST_TYPE_CALL: if (ast->function.callconv != 0) { ccb_compile_warn(ccb, "Invoking function with non-standard calling convention %d", ast->function.callconv); callconv = ast->function.callconv; } else { callconv = 0; } /* At least for "__classic_call", if the return type won't naturally fit in the return registers then * a pointer to an area reserved for the result will have been inserted beneath the first regular argument. * So we just need to increment the spare arguments offset by one word in that case! * TODO: Store the offset of that argument somewhere to make sure other code doesn't get it wrong? */ if (callconv == 101 && ast->ctype->size > ccb_target_wordbytes(ccb)*2) { ccb_compile_warn(ccb, "Invoking function with experimental large return value support!"); largeresult = 1; } else { largeresult = 0; } int resultstack = 0; if (largeresult) { int x; // TODO: Initialise the register before pushing so it's not initialising it with junk! if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi %s, zero, 0", ccb_target_r0(ccb)); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, 0", ccb_target_r0(ccb)); } else { ccb_target_gen_emit("mov $0, %%%s", ccb_target_r0(ccb)); } for (x = 0; x < ast->ctype->size; x += ccb_target_wordbytes(ccb)) { ccb_target_gen_push(ccb_target_r0(ccb)); } resultstack = ccb_target_gen_stack; } argtypes = ccb_target_gen_function_argument_types(ccb, ast); for (ccb_list_iterator_t* it = ccb_list_iterator(argtypes); !ccb_list_iterator_end(it); ) { ccb_data_type_t* tmp = ccb_list_iterator_next(it); if (ccb_ast_type_floating(ccb, tmp) && callconv != 101) { if (regx > 0) ccb_target_gen_push_xmm(regx); regx++; nregfloats++; } else if (ccb_ast_type_floating(ccb, tmp)) { nstackints++; //nstackfloats++; NOTE: Seems to be easier to just track them as ints! } else if (tmp->size > ccb_target_wordbytes(ccb)) { long s = ccb_target_wordaligned(ccb, tmp->size); nstackints += s/ccb_target_wordbytes(ccb); } else { if (callconv != 101 && regi < ccb_target_callregisters(ccb)) { const char* re = ccb_target_callregister(ccb, regi++); if (ccb_strcasecmp(re, ccb_target_r0(ccb)) == 0) { // Avoid pushing/popping the result register! } else { //XXX TODO: Something like this MIGHT be needed sometimes but otherwise complicates output: //ccb_target_gen_push(re); } nregints++; } else { // This will go on the stack so it doesn't need a register! nstackints++; } } } // TODO: Consider nstackints/nstackfloats in stack alignment! int old_stack = ccb_target_gen_stack; if (((ccb_target_gen_stack + ((nstackints + nstackfloats + largeresult) * 8)) % 16) != 0) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("subrc r4, 8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("addimm $rsp, $rsp, -8"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi sp, sp, -8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("sub rsp, 8"); } else { ccb_target_gen_emit("sub $8, %%rsp"); } ccb_target_gen_stack += 8; } istackints = nstackints; istackfloats = nstackfloats; //fprintf(stderr, "test1\n"); ccb_list_iterator_t* itt = ccb_list_iterator(ccb_list_reverse(argtypes)); for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_list_reverse(ast->function.call.args)); !ccb_list_iterator_end(it); ) { ccb_ast_t* v = ccb_list_iterator_next(it); ccb_data_type_t* t = ccb_list_iterator_next(itt); /*if (ccb_ast_type_floating(ccb, v)) { //ccb_target_gen_pop_xmm(--backx); } else {*/ if (istackints > 0) { ccb_target_gen_expression(ccb, v); ccb_target_gen_save(ccb, /*ptype*/ /*v->ctype*/ t, v->ctype); //ccb_target_gen_push_xmm(0); if (ccb_ast_type_floating(ccb, v->ctype)) { // TODO: I think I'm using this function wrong... istackints--; fprintf(stderr, "Pushing 1 float\n"); //ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_push_xmm(0); } else if (t->size > ccb_target_wordbytes(ccb)) { if (t->size <= ccb_target_wordbytes(ccb)*2) { istackints -= 2; ccb_target_gen_push(ccb_target_r1(ccb)); ccb_target_gen_push(ccb_target_r0(ccb)); } else { // Hopefully it has already been pushed! Just calculate the correct number of stack slots long s = ccb_target_wordaligned(ccb, t->size); istackints -= s/ccb_target_wordbytes(ccb); } } else { istackints--; //ccb_data_type_t* ptype = ccb_list_iterator_next(jt); //ccb_target_gen_save(ccb, ptype, v->ctype); ccb_target_gen_push(ccb_target_r0(ccb)); } } else { //ccb_target_gen_pop(ccb_target_callregister(ccb, --backi)); } //} } istackints = nstackints; istackfloats = nstackfloats; iregints = nregints; iregfloats = nregfloats; //fprintf(stderr, "test2\n"); ccb_list_iterator_t* jt = ccb_list_iterator(argtypes); for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.call.args); !ccb_list_iterator_end(it);) { ccb_ast_t* v = ccb_list_iterator_next(it); ccb_data_type_t* ptype = ccb_list_iterator_next(jt); if (callconv != 101 && ccb_ast_type_floating(ccb, ptype)) { ccb_target_gen_expression(ccb, v); ccb_target_gen_save(ccb, ptype, v->ctype); ccb_target_gen_push_xmm(0); } else { /*if (istackints > 0) { istackints--; } else*/ if (nregints > 0) { ccb_target_gen_expression(ccb, v); ccb_target_gen_save(ccb, ptype, v->ctype); ccb_target_gen_push(ccb_target_r0(ccb)); nregints--; } } } /* For pointer calls, calculate target address after pushing args but before moving them to registers: */ if (ast->type == CCB_AST_TYPE_PTRCALL) { ccb_target_gen_expression(ccb, ast->function.call.callable); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_pop(ccb_target_r15(ccb)); } backi = regi; backx = regx; istackints = nstackints; //fprintf(stderr, "test3\n"); for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_list_reverse(argtypes)); !ccb_list_iterator_end(it); ) { ccb_data_type_t* t = ccb_list_iterator_next(it); if (ccb_ast_type_floating(ccb, t) && callconv != 101) { ccb_target_gen_pop_xmm(--backx); } else { if (istackints > 0) { long s = ccb_target_wordaligned(ccb, t->size); istackints-= s/ccb_target_wordbytes(ccb); } else { ccb_target_gen_pop(ccb_target_callregister(ccb, --backi)); } } } if (largeresult) { int diff = ccb_target_gen_stack - resultstack; if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi %s, sp, %d", ccb_target_r0(ccb), diff); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, rsp", ccb_target_r0(ccb)); ccb_target_gen_emit("add %s, %d", ccb_target_r0(ccb), diff); } else { ccb_target_gen_emit("mov %%rsp, %%%s", ccb_target_r0(ccb)); ccb_target_gen_emit("add $%d, %%%s", diff, ccb_target_r0(ccb)); } ccb_target_gen_push(ccb_target_r0(ccb)); } // TODO : I forget wtf was going on here... /* if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %d", regx); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $r0, $r0, $r0"); ccb_target_gen_emit("addimm $r0, $r0, %d", regx); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("li a0, %d", regx); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { //ccb_target_gen_emit("addi a0, zero, %d", regx); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov eax, %d", regx); // TODO: Should this be rax?? } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0x48, 0xB8 ; mov rax, ..."); ccb_target_gen_emit("data64 %d", regx); } else { ccb_target_gen_emit("mov $%d, %%eax", regx); }*/ if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_callconv(ccb) == CCB_TARGET_CALLCONV_WINDOWS) { // TODO: Fix/test/update this stuff so that __classic_call works on Windows too. ccb_target_gen_emit("push 0"); ccb_target_gen_emit("push 0"); ccb_target_gen_emit("push 0"); ccb_target_gen_emit("push 0"); } if (ast->type == CCB_AST_TYPE_PTRCALL) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("callr r15"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("be $rlink, $r0, $rF"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("jalr t0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("call r15"); } else { ccb_target_gen_emit("call *%%r15 # GNU syntax is a fucking disaster"); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("callc %s", ast->function.name); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("xor $rF, $rF, $rF"); ccb_target_gen_emit("addimm $rF, $rF, %s", ast->function.name); ccb_target_gen_emit("be $rlink, $r0, $rF"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("call %s", ast->function.name); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("bl %s", ast->function.name); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { tmpintctr++; ccb_target_gen_emit("data8 0xE8 ; call ..."); ccb_target_gen_emit("data32 (%s - callinstrend%i) ; instruction-relative address", ast->function.name, tmpintctr); ccb_target_gen_emit("callinstrend%i:", tmpintctr); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { //NOTE: Redeclaring externs with each call does NOT work with FASM if (!ccb_extrastage_ispredeclared(ccb, ast->function.name)) { ccb_target_gen_emit("extrn %s", ast->function.name); ccb_extrastage_setpredeclared(ccb, ast->function.name); } ccb_target_gen_emit("call %s", ast->function.name); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (!ccb_extrastage_ispredeclared(ccb, ast->function.name)) { ccb_target_gen_emit("extern %s", ast->function.name); ccb_extrastage_setknownextern(ccb, ast->function.name); ccb_extrastage_setpredeclared(ccb, ast->function.name); } /* Position-independent code now works with NASM */ if (ccb_extrastage_ispredeclared(ccb, ast->function.name) && !ccb_extrastage_isknownextern(ccb, ast->function.name)) { ccb_target_gen_emit("call %s", ast->function.name); } else { ccb_target_gen_emit("call %s wrt ..plt", ast->function.name); } //ccb_target_gen_emit("call [rel %s wrt ..got]", ast->function.name); } else { ccb_target_gen_emit("call %s", ast->function.name); } } istackints = nstackints; istackfloats = nstackfloats; //fprintf(stderr, "test4\n"); if (largeresult) { ccb_target_gen_drop(); } for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_list_reverse(argtypes)); !ccb_list_iterator_end(it); ) { ccb_data_type_t* t = ccb_list_iterator_next(it); if (ccb_ast_type_floating(ccb, t) && callconv != 101) { /*if (regx != 1) ccb_target_gen_pop_xmm(--regx);*/ } else { if (istackints > 0) { long s = ccb_target_wordaligned(ccb, t->size); istackints -= s/ccb_target_wordbytes(ccb); while (s > 0) { //ccb_target_gen_pop(ccb_target_r1/*15*/(ccb)); // TODO: Add a "drop" equivalent // BROKEN? ccb_target_gen_drop(); s -= ccb_target_wordbytes(ccb); } } else { /*const char* re = ccb_target_callregister(ccb, --regi); if (ccb_strcasecmp(re, ccb_target_r0(ccb)) == 0) { // Avoid pushing/popping the result register! } else { ccb_target_gen_pop(re); }*/ } } } if (((old_stack + ((nstackints + nstackfloats + largeresult) * 8)) % 16) != 0) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrc r4, 8"); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi sp, sp, 8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("add rsp, 8"); } else { ccb_target_gen_emit("add $8, %%rsp"); } ccb_target_gen_stack -= 8; } istackints = nstackints; istackfloats = nstackfloats; //fprintf(stderr, "test5\n"); for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_list_reverse(argtypes)); !ccb_list_iterator_end(it); ) { ccb_data_type_t* t = ccb_list_iterator_next(it); if (ccb_ast_type_floating(ccb, t) && callconv != 101) { if (regx != 1) ccb_target_gen_pop_xmm(--regx); } else { if (istackints > 0) { long s = ccb_target_wordaligned(ccb, t->size); istackints -= s/ccb_target_wordbytes(ccb); //istackints--; //ccb_target_gen_pop("r15"); // TODO: Add a "drop" equivalent } else { const char* re = ccb_target_callregister(ccb, --regi); if (ccb_strcasecmp(re, ccb_target_r0(ccb)) == 0) { // Avoid pushing/popping the result register! } else { //XXX TODO: Something like this MIGHT be needed sometimes but otherwise complicates output: //ccb_target_gen_pop(re); } } } } /* Finally, decode the floating-point result for a __classic_call invocation */ if (callconv == 101 && ccb_ast_type_floating(ccb, ast->ctype)) { ccb_compile_warn(ccb, "Generating invocation of special floating point return hack for __classic_call"); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_pop_xmm(0); } if (ast->ctype->type == CCB_TYPE_FLOAT) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("addrr r0, r1"); // TODO } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("fcvt.d.s fa0, fa0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cvtps2pd xmm0, xmm0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86) { ccb_target_gen_emit("cvtps2pd %%xmm0, %%xmm0"); } else { ccb_target_gen_emit("todo"); } } if (ast->type == CCB_AST_TYPE_PTRCALL) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_pop("t0"); } else { ccb_target_gen_pop("r15"); } } break; case CCB_AST_TYPE_DECLARATION: if (ast->decl.init) ccb_target_gen_declaration_initialization(ccb, ast->decl.init, ast->decl.var->variable.off); break; case CCB_AST_TYPE_ADDRESS: switch (ast->unary.operand->type) { case CCB_AST_TYPE_FUNCTION: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %s", ast->unary.operand->function.name); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("lui a0, %%hi(%s)", ast->unary.operand->function.name); //ccb_target_gen_emit("addi a0, a0, %%lo(%s)", ast->unary.operand->function.name); ccb_target_gen_emit("la a0, %s", ast->unary.operand->function.name); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s]", ast->unary.operand->function.name); } else { ccb_target_gen_emit("lea %s(%%rip), %%rax", ast->unary.operand->function.name); } break; case CCB_AST_TYPE_VAR_LOCAL: ccb_target_gen_ensure_lva(ccb, ast->unary.operand); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrrpc r0, r5, %d", ast->unary.operand->variable.off); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("add a0, fp, %d", ast->unary.operand->variable.off); //ccb_target_gen_emit("addi a0, x8, %d", ast->unary.operand->variable.off); ccb_target_gen_rv_addi(ccb, "a0", "x8", ast->unary.operand->variable.off); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [rbp + %d]", ast->unary.operand->variable.off); } else { ccb_target_gen_emit("lea %d(%%rbp), %%rax", ast->unary.operand->variable.off); } break; case CCB_AST_TYPE_VAR_GLOBAL: if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrc r0, %s", ast->unary.operand->variable.label); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("lea rax, [%s]", ast->unary.operand->variable.label); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("lui a0, %%hi(%s)", ast->unary.operand->variable.label); //ccb_target_gen_emit("addi a0, a0, %%lo(%s)", ast->unary.operand->variable.label); ccb_target_gen_emit("la a0, %s", ast->unary.operand->variable.label); } else { ccb_target_gen_emit("lea %s(%%rip), %%rax", ast->unary.operand->variable.label); } break; case CCB_AST_TYPE_STRUCT: ccb_target_gen_load_structure(ccb, ast->structure, ast->ctype, 0, 1); break; case CCB_AST_TYPE_DEREFERENCE: ccb_target_gen_expression(ccb, ast->unary.operand); break; default: ccb_compile_error(ccb, "Internal error in gen_expression, trying to get address of type 0x100+%d", ast->unary.operand->type-0x100); break; } break; case CCB_AST_TYPE_DEREFERENCE: ccb_target_gen_expression(ccb, ast->unary.operand); ccb_target_gen_load_local(ccb, ast->unary.operand->ctype->pointer, ccb_target_r0(ccb), 0); ccb_target_gen_load(ccb, ast->ctype, ast->unary.operand->ctype->pointer); break; case CCB_AST_TYPE_STATEMENT_IF: case CCB_AST_TYPE_EXPRESSION_TERNARY: ccb_target_gen_expression(ccb, ast->ifstmt.cond); ne = ccb_ast_label(ccb); ccb_target_gen_je(ccb, ne); ccb_target_gen_expression(ccb, ast->ifstmt.then); if (ast->ifstmt.last) { end = ccb_ast_label(ccb); ccb_target_gen_jmp(ccb, end); ccb_target_gen_label(ccb, ne); //ccb_target_gen_emit("# TEST LINE C 1"); ccb_target_gen_expression(ccb, ast->ifstmt.last); //ccb_target_gen_emit("# TEST LINE C 2"); ccb_target_gen_label(ccb, end); } else { ccb_target_gen_label(ccb, ne); } break; case CCB_AST_TYPE_STATEMENT_FOR: { if (ast->forstmt.init) ccb_target_gen_expression(ccb, ast->forstmt.init); begin = ccb_ast_label(ccb); step = ccb_ast_label(ccb); end = ccb_ast_label(ccb); ccb_target_gen_jump_save(ccb, end, step); ccb_target_gen_label(ccb, begin); if (ast->forstmt.cond) { ccb_target_gen_expression(ccb, ast->forstmt.cond); ccb_target_gen_je(ccb, end); } ccb_target_gen_expression(ccb, ast->forstmt.body); ccb_target_gen_label(ccb, step); if (ast->forstmt.step) ccb_target_gen_expression(ccb, ast->forstmt.step); ccb_target_gen_jmp(ccb, begin); ccb_target_gen_label(ccb, end); ccb_target_gen_jump_restore(ccb); } break; case CCB_AST_TYPE_STATEMENT_WHILE: { begin = ccb_ast_label(ccb); end = ccb_ast_label(ccb); ccb_target_gen_jump_save(ccb, end, begin); ccb_target_gen_label(ccb, begin); ccb_target_gen_expression(ccb, ast->forstmt.cond); ccb_target_gen_je(ccb, end); ccb_target_gen_expression(ccb, ast->forstmt.body); ccb_target_gen_jmp(ccb, begin); ccb_target_gen_label(ccb, end); ccb_target_gen_jump_restore(ccb); } break; case CCB_AST_TYPE_STATEMENT_DO: { begin = ccb_ast_label(ccb); end = ccb_ast_label(ccb); //fprintf(stderr, "generated begin '%s' end '%s'\n", begin, end); ccb_target_gen_jump_save(ccb, end, begin); ccb_target_gen_label(ccb, begin); ccb_target_gen_expression(ccb, ast->forstmt.body); ccb_target_gen_expression(ccb, ast->forstmt.cond); //ccb_target_gen_emit("#TODO TEST LINE 1"); ccb_target_gen_je(ccb, end); ccb_target_gen_jmp(ccb, begin); ccb_target_gen_label(ccb, end); ccb_target_gen_jump_restore(ccb); //ccb_target_gen_emit("#TODO TEST LINE 2"); } break; case CCB_AST_TYPE_STATEMENT_BREAK: if (!ccb_target_gen_label_break) ccb_compile_error(ccb, "ICE"); ccb_target_gen_jmp(ccb, ccb_target_gen_label_break); break; case CCB_AST_TYPE_STATEMENT_CONTINUE: if (!ccb_target_gen_label_continue) ccb_compile_error(ccb, "ICE"); ccb_target_gen_jmp(ccb, ccb_target_gen_label_continue); break; case CCB_AST_TYPE_STATEMENT_SWITCH: { char* ccb_target_gen_label_switch_store = ccb_target_gen_label_switch; char* ccb_target_gen_label_break_store = ccb_target_gen_label_break; ccb_target_gen_expression(ccb, ast->switchstmt.expr); ccb_target_gen_label_switch = ccb_ast_label(ccb); ccb_target_gen_label_break = ccb_ast_label(ccb); //fprintf(stderr, "generated switch '%s' break '%s'\n", ccb_target_gen_label_switch, ccb_target_gen_label_break); ccb_target_gen_jmp(ccb, ccb_target_gen_label_switch); ccb_target_gen_expression(ccb, ast->switchstmt.body); ccb_target_gen_label(ccb, ccb_target_gen_label_switch); //ccb_target_gen_emit("#TODO TEST LINE B 1"); ccb_target_gen_label(ccb, ccb_target_gen_label_break); //ccb_target_gen_emit("# TODO TEST LINE B 2"); ccb_target_gen_label_switch = ccb_target_gen_label_switch_store; ccb_target_gen_label_break = ccb_target_gen_label_break_store; } break; case CCB_AST_TYPE_STATEMENT_CASE: if (!ccb_target_gen_label_switch) ccb_compile_error(ccb, "ICE"); skip = ccb_ast_label(ccb); ccb_target_gen_jmp(ccb, skip); ccb_target_gen_label(ccb, ccb_target_gen_label_switch); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprr r0, %d", ast->casevalue); // TODO: Is this the right ordering?? } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi a1, zero, %d", ast->casevalue); ccb_target_gen_emit("li a1, %d", ast->casevalue); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cmp eax, %d", ast->casevalue); } else { ccb_target_gen_emit("cmp $%d, %%eax", ast->casevalue); } ccb_target_gen_label_switch = ccb_ast_label(ccb); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("jumpcifne %s", ccb_target_gen_label_switch); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("bne a0, a1, %s", ccb_target_gen_label_switch); } else { ccb_target_gen_emit("jne %s", ccb_target_gen_label_switch); } ccb_target_gen_label(ccb, skip); break; case CCB_AST_TYPE_STATEMENT_DEFAULT: if (!ccb_target_gen_label_switch) ccb_compile_error(ccb, "ICE"); ccb_target_gen_label(ccb, ccb_target_gen_label_switch); ccb_target_gen_label_switch = ccb_ast_label(ccb); break; case CCB_AST_TYPE_STATEMENT_GOTO: ccb_target_gen_jmp(ccb, ast->gotostmt.where); break; case CCB_AST_TYPE_STATEMENT_LABEL: if (ast->gotostmt.where) ccb_target_gen_label(ccb, ast->gotostmt.where); break; /* case CCB_AST_TYPE_STATEMENT_GOTO: fprintf(stderr, "Generating goto '%s'\n", ast->gotostmt.label); ccb_target_gen_jmp(ccb, ast->gotostmt.label); break; case CCB_AST_TYPE_STATEMENT_LABEL: fprintf(stderr, "Generating label '%s'\n", ast->gotostmt.label); ccb_target_gen_label(ccb, ast->gotostmt.label); break; */ case CCB_AST_TYPE_STATEMENT_RETURN: if (ast->returnstmt) { ccb_target_gen_expression(ccb, ast->returnstmt); ccb_target_gen_save(ccb, ast->ctype, ast->returnstmt->ctype); /* For __classic_call, we emulate "soft float" style conventions - always using integer registers for the result. */ if (ast->return_callconv == 101 && ccb_ast_type_floating(ccb, ast->ctype)) { ccb_compile_warn(ccb, "Generating experimental floating point return for __classic_call"); ccb_target_gen_push_xmm(0); ccb_target_gen_pop(ccb_target_r0(ccb)); } else if (ast->return_callconv == 101 && ast->ctype->size > ccb_target_wordbytes(ccb)*2) { ccb_compile_warn(ccb, "Generating experimental large value return for __classic_call"); // TODO: This could all be cleaned up a lot (I somehow forgot indirection at first hence the longer code..), but it seems to WORK! so for now I'm keeping it if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi %s, x8, %d", ccb_target_r0(ccb), ccb_target_type_size_pointer(ccb)*2); ccb_target_gen_emit("ld %s, 0(%s)", ccb_target_r1(ccb), ccb_target_r0(ccb)); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov %s, rbp", ccb_target_r0(ccb)); ccb_target_gen_emit("add %s, %d", ccb_target_r0(ccb), ccb_target_type_size_pointer(ccb)*2); ccb_target_gen_emit("movq %s, [%s]", ccb_target_r1(ccb), ccb_target_r0(ccb)); } else { ccb_target_gen_emit("mov %%rbp, %%%s", ccb_target_r0(ccb)); ccb_target_gen_emit("add $%d, %%%s", ccb_target_type_size_pointer(ccb)*2, ccb_target_r0(ccb)); ccb_target_gen_emit("movq 0(%%%s), %%%s", ccb_target_r0(ccb), ccb_target_r1(ccb)); } ccb_target_gen_save_dolongstore(ccb, ast->ctype, ccb_target_r1(ccb), 0, 0); } } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_pop("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("li a0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rax, 0"); } else { ccb_target_gen_emit("mov $0, %%rax"); } } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r4, r5"); ccb_target_gen_emit("popr r5"); ccb_target_gen_emit("return"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_pop("$rlink"); ccb_target_gen_emit("bto $rlink, $rlink, $rlink"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi sp, x8, 0"); ccb_target_gen_pop(ccb_target_bp(ccb)); ccb_target_gen_pop("ra"); ccb_target_gen_emit("ret"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("ldp x29, x30, [sp], #32"); //ccb_target_gen_emit("add sp, sp, 16"); ccb_target_gen_emit("ret"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0xC9 ; leave"); ccb_target_gen_emit("data8 0xC3 ; ret"); } else { ccb_target_gen_emit("leave"); ccb_target_gen_emit("ret"); } break; case CCB_AST_TYPE_STATEMENT_COMPOUND: for (ccb_list_iterator_t* it = ccb_list_iterator(ast->compound); !ccb_list_iterator_end(it); ) { ccb_target_gen_statement(ccb, ccb_list_iterator_next(it)); } break; case CCB_AST_TYPE_STATEMENT_ASM: for (ccb_list_iterator_t* it = ccb_list_iterator(ast->asmstmt.code); !ccb_list_iterator_end(it); ) { ccb_target_gen_emit("%s", ccb_list_iterator_next(it)); } break; case CCB_AST_TYPE_STRUCT: ccb_target_gen_load_structure(ccb, ast->structure, ast->ctype, 0, 0); break; case '!': ccb_target_gen_expression(ccb, ast->unary.operand); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("setrflageq r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("not a0, a0"); //ccb_target_gen_emit("andi a0, a0, 1"); ccb_target_gen_emit("seqz a0, a0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("cmp rax, 0"); ccb_target_gen_emit("sete al"); ccb_target_gen_emit("movzx eax, al"); } else { ccb_target_gen_emit("cmp $0, %%rax"); ccb_target_gen_emit("sete %%al"); ccb_target_gen_emit("movzx %%al, %%eax"); //ccb_target_gen_emit("movzb %%al, %%eax"); } break; case CCB_AST_TYPE_AND: end = ccb_ast_label(ccb); ccb_target_gen_expression(ccb, ast->left); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("setrc r0, 0"); ccb_target_gen_emit("jumpcifeq %s", end); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("mv a1, a0"); ccb_target_gen_emit("addi a2, zero, 0"); ccb_target_gen_emit("addi a0, zero, 0"); ccb_target_gen_emit("beq a1, a2, %s", end); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("test rax, rax"); ccb_target_gen_emit("mov rax, 0"); ccb_target_gen_emit("je %s", end); } else { ccb_target_gen_emit("test %%rax, %%rax"); ccb_target_gen_emit("mov $0, %%rax"); ccb_target_gen_emit("je %s", end); } ccb_target_gen_expression(ccb, ast->right); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("setrc r0, 0"); ccb_target_gen_emit("jumpcifeq %s", end); ccb_target_gen_emit("setrc r0, 1"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("mv a1, a0"); ccb_target_gen_emit("addi a2, zero, 0"); ccb_target_gen_emit("addi a0, zero, 0"); ccb_target_gen_emit("beq a1, a2, %s", end); ccb_target_gen_emit("addi a0, zero, 1"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("test rax, rax"); ccb_target_gen_emit("mov rax, 0"); ccb_target_gen_emit("je %s", end); ccb_target_gen_emit("mov rax, 1"); } else { ccb_target_gen_emit("test %%rax, %%rax"); ccb_target_gen_emit("mov $0, %%rax"); ccb_target_gen_emit("je %s", end); ccb_target_gen_emit("mov $1, %%rax"); } ccb_target_gen_label(ccb, end); break; case CCB_AST_TYPE_OR: end = ccb_ast_label(ccb); ccb_target_gen_expression(ccb, ast->left); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("setrc r0, 1"); ccb_target_gen_emit("jumpcifne %s", end); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("mv a1, a0"); ccb_target_gen_emit("addi a2, zero, 0"); ccb_target_gen_emit("addi a0, zero, 1"); ccb_target_gen_emit("bne a1, a2, %s", end); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("test rax, rax"); ccb_target_gen_emit("mov rax, 1"); ccb_target_gen_emit("jne %s", end); } else { ccb_target_gen_emit("test %%rax, %%rax"); ccb_target_gen_emit("mov $1, %%rax"); ccb_target_gen_emit("jne %s", end); } ccb_target_gen_expression(ccb, ast->right); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("comprc r0, 0"); ccb_target_gen_emit("setrc r0, 1"); ccb_target_gen_emit("jumpcifne %s", end); ccb_target_gen_emit("setrc r0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("mv a1, a0"); ccb_target_gen_emit("addi a2, zero, 0"); ccb_target_gen_emit("addi a0, zero, 1"); ccb_target_gen_emit("bne a1, a2, %s", end); ccb_target_gen_emit("addi a0, zero, 0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("test rax, rax"); ccb_target_gen_emit("mov rax, 1"); ccb_target_gen_emit("jne %s", end); ccb_target_gen_emit("mov rax, 0"); // Fix, I think I mistranslated at first.. } else { ccb_target_gen_emit("test %%rax, %%rax"); ccb_target_gen_emit("mov $1, %%rax"); ccb_target_gen_emit("jne %s", end); ccb_target_gen_emit("mov $0, %%rax"); } ccb_target_gen_label(ccb, end); break; case '&': case '|': ccb_target_gen_expression(ccb, ast->left); ccb_target_gen_push(ccb_target_r0(ccb)); ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_pop(ccb_target_r1(ccb)); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("%s r0, r1", (ast->type == '|') ? "orrr" : "andrr"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("%s a0, a0, a1", (ast->type == '|') ? "or" : "and"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("%s rax, rcx", (ast->type == '|') ? "or" : "and"); } else { ccb_target_gen_emit("%s %%rcx, %%rax", (ast->type == '|') ? "or" : "and"); } break; case '~': ccb_target_gen_expression(ccb, ast->left); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("notr r0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("not a0, a0"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { const char* r = ccb_target_gen_register_integer(ccb, ast->ctype, 'a'); ccb_target_gen_emit("not %s", r); // TODO: The actual issue here may be with sign extension (at least one test suite failure...) //ccb_target_gen_emit("xor rax, -1"); } else { const char* r = ccb_target_gen_register_integer(ccb, ast->ctype, 'a'); ccb_target_gen_emit("not %%%s", r); // TODO: The actual issue here may be with sign extension (at least one test suite failure...) //ccb_target_gen_emit("xor %%rax, -1"); } break; case CCB_AST_TYPE_POST_INCREMENT: ccb_target_gen_emit_postfix(ccb, ast, "add"); break; case CCB_AST_TYPE_POST_DECREMENT: ccb_target_gen_emit_postfix(ccb, ast, "sub"); break; case CCB_AST_TYPE_PRE_INCREMENT: ccb_target_gen_emit_prefix(ccb, ast, "add"); break; case CCB_AST_TYPE_PRE_DECREMENT: ccb_target_gen_emit_prefix(ccb, ast, "sub"); break; case CCB_AST_TYPE_EXPRESSION_CAST: ccb_target_gen_expression(ccb, ast->unary.operand); if (ast->ctype->sign && ast->unary.operand->ctype->type == CCB_AST_DATA_INT && ast->unary.operand->ctype->sign && ast->unary.operand->ctype->size == 4) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addw a0, a0, zero"); // Sign extend by using a 32-bit addition } } ccb_target_gen_load(ccb, ast->ctype, ast->unary.operand->ctype); break; case '=': ccb_target_gen_expression(ccb, ast->right); ccb_target_gen_load(ccb, ast->ctype, ast->right->ctype); ccb_target_gen_assignment(ccb, ast->left); /* An assigment's result is expected to be the assigned value, so for structs we need to reconstruct a copy of the value */ if (ast->right->ctype->size > ccb_target_wordbytes(ccb)) { ccb_compile_warn(ccb, "Pushing/reconstructing result... of size %d", ast->right->ctype->size); //TODO: Make this "dup" the value or something instead... ccb_target_gen_expression(ccb, ast->left); } break; case CCB_AST_TYPE_EXPRESSION_COMMA: ccb_target_gen_expression(ccb, ast->left); /* TODO: Drop result of first expression from stack for values larger than a word? */ ccb_target_gen_expression(ccb, ast->right); break; default: ccb_target_gen_binary(ccb, ast); } } static void ccb_target_gen_data_initialization_intermediate(ccb_t* ccb, ccb_table_t* labels, char* data, ccb_table_t* literal, ccb_list_t* init, int offset); static void ccb_target_gen_data_initialization_intermediate(ccb_t* ccb, ccb_table_t* labels, char* data, ccb_table_t* literal, ccb_list_t* init, int offset) { for (ccb_list_iterator_t* it = ccb_list_iterator(init); !ccb_list_iterator_end(it); ) { ccb_ast_t* node = ccb_list_iterator_next(it); //printf("Iterating over node at 0x%lx (init.value at 0x%lx)\n", node, node->init.value); //printf("Unary operand at 0x%lx \n", node->init.value->unary.operand); // TODO: Doesn't properly optimise away the "&&" here so we have to triple-if it: if ((node->init.value->type == CCB_AST_TYPE_ADDRESS)) if( (node->init.value->unary.operand->type == CCB_AST_TYPE_VAR_LOCAL)) if ( (node->init.value->unary.operand->variable.init)) { //printf("address\n"); char* label = ccb_ast_label(ccb); ccb_string_t* string = ccb_string_create(); //ccb_string_catf(string, "%d", node->init.offset + offset); ccb_string_catint(string, node->init.offset + offset); ccb_table_insert(literal, label, node->init.value->unary.operand); ccb_table_insert(labels, ccb_string_buffer(string), label); continue; } //printf("...\n"); if (node->init.value->type == CCB_AST_TYPE_VAR_LOCAL && node->init.value->variable.init) { ccb_target_gen_data_initialization_intermediate(ccb, labels, data, literal, node->init.value->variable.init, node->init.offset + offset); continue; } //printf("...\n"); //printf("Using %d\n", node->init.type->type); switch (node->init.type->type) { case CCB_TYPE_FLOAT: *(float*)(data + node->init.offset + offset) = node->init.value->floating.value; break; case CCB_TYPE_DOUBLE: *(double*)(data + node->init.offset + offset) = node->init.value->floating.value; break; case CCB_TYPE_CHAR: *(char*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); break; case CCB_TYPE_SHORT: *(short*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); break; case CCB_TYPE_INT: *(int*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); break; case CCB_TYPE_LONG: *(long*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); break; case CCB_TYPE_LLONG: *(long long*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); break; case CCB_TYPE_POINTER: *(long*)(data + node->init.offset + offset) = ccb_parse_evaluate(ccb, node->init.value); default: break; } } } static void ccb_target_gen_data_initialization(ccb_t* ccb, ccb_table_t* table, ccb_list_t* list, int size) { char* data = ccb_memory_allocate(size); memset(data, 0, size); ccb_table_t* labels = ccb_table_create(NULL); ccb_target_gen_data_initialization_intermediate(ccb, labels, data, table, list, 0); int i = 0; for (; i <= size - 4; i += 4) { ccb_string_t* string = ccb_string_create(); //ccb_string_catf(string, "%d", i); ccb_string_catint(string, i); char* label = ccb_table_find(labels, ccb_string_buffer(string)); if (label) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit("data64 %s", label); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("dq %s", label); } else { ccb_target_gen_emit(".quad %s", label); } i += 4; } else { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit("data32 %d", data[i]); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("dd %d", data[i]); } else { //ccb_target_gen_emit(".long %d", data[i]); ccb_target_gen_emit(".byte %d", data[i]); ccb_target_gen_emit(".byte %d", data[i+1]); ccb_target_gen_emit(".byte %d", data[i+2]); ccb_target_gen_emit(".byte %d", data[i+3]); } } } for (; i < size; i++) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit("data8 %d", data[i]); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("db %d", data[i]); } else { ccb_target_gen_emit(".byte %d", data[i]); } } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit("align 8"); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("align 8"); } else { ccb_target_gen_emit(".align 8"); } } /* * Recursive compliteral generation, emits data initialization * until there is nothing to initialize left. */ static void ccb_target_gen_data_literal(ccb_t* ccb, char* label, ccb_ast_t* ast) { ccb_table_t* table = ccb_table_create(NULL); ccb_target_gen_emit_inline("%s:", label); ccb_target_gen_data_initialization(ccb, table, ast->variable.init, ast->ctype->size); for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_keys(table)); !ccb_list_iterator_end(it); ) { char* label = ccb_list_iterator_next(it); ccb_ast_t* node = ccb_table_find(table, label); ccb_target_gen_data_literal(ccb, label, node); } } static void ccb_target_gen_localorglobal(ccb_t* ccb, ccb_ast_t* ast) { if (!ast->decl.var->ctype->isstatic) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_inline("symbol %s, public", ast->decl.var->variable.label); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [public %s]", ast->decl.var->variable.label); } else { ccb_target_gen_emit_inline("public %s", ast->decl.var->variable.label); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [public %s]", ast->decl.var->variable.label); } else { ccb_target_gen_emit_inline("global %s", ast->decl.var->variable.label); } } else { ccb_target_gen_emit_inline(".global %s", ast->decl.var->variable.label); } } } static void ccb_target_gen_data(ccb_t* ccb, ccb_ast_t* ast) { ccb_table_t* table = ccb_table_create(NULL); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_inline("section data"); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { // ignored for flat binaries for now } else { ccb_target_gen_emit_inline("section '.data' writable"); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { // ignored for flat binaries for now } else { ccb_target_gen_emit_inline("section .data"); } } else { ccb_target_gen_emit_inline(".data"); } ccb_target_gen_localorglobal(ccb, ast); ccb_target_gen_emit_inline("%s:", ast->decl.var->variable.label); ccb_target_gen_data_initialization(ccb, table, ast->decl.init, ast->decl.var->ctype->size); for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_keys(table)); !ccb_list_iterator_end(it); ) { char* label = ccb_list_iterator_next(it); ccb_ast_t* node = ccb_table_find(table, label); ccb_target_gen_data_literal(ccb, label, node); } } static void ccb_target_gen_bss(ccb_t* ccb, ccb_ast_t* ast) { ccb_target_gen_localorglobal(ccb, ast); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit_inline("section zerodata"); ccb_target_gen_emit("align %d", ast->decl.var->ctype->size < 8 ? ast->decl.var->ctype->size : 8); ccb_target_gen_emit_inline("%s:", ast->decl.var->variable.label); ccb_target_gen_emit("reserve %d", ast->decl.var->ctype->size); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [bss section]"); } else { ccb_target_gen_emit_inline("section '.bss' writeable"); } ccb_target_gen_emit("align %d", ast->decl.var->ctype->size < 8 ? ast->decl.var->ctype->size : 8); /* I thought some errors in the FASM output were due to reserving rather than setting bytes explicitly, * turns out that wasn't the case (I was missing the "section" directive above!). However, the * explicitly-setting code is preserved here in case it's useful in the future (e.g. for targetting * assemblers which can't reserve bytes like FASM). switch (ast->decl.var->ctype->size) { case 1: ccb_target_gen_emit("%s: db 0", ast->decl.var->variable.name); break; case 2: ccb_target_gen_emit("%s: dw 0", ast->decl.var->variable.name); break; case 4: ccb_target_gen_emit("%s: dd 0", ast->decl.var->variable.name); break; case 8: ccb_target_gen_emit("%s: dq 0", ast->decl.var->variable.name); break; default: ccb_target_gen_emit("%s:", ast->decl.var->variable.name); int i; for (i = 0; i < ast->decl.var->ctype->size; i++) { ccb_target_gen_emit(" db 0"); } } */ // Simply reserve the required number of bytes ccb_target_gen_emit("%s: rb %d", ast->decl.var->variable.label, ast->decl.var->ctype->size); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [bss section]"); } else { ccb_target_gen_emit_inline("section .bss"); } long alignment = ast->decl.var->ctype->size < 8 ? ast->decl.var->ctype->size : 8; while ((ccb->bsscount % alignment) != 0) { ccb_target_gen_emit("resb 1 ; Manual padding for alignment"); // TODO: See if this actually makes a difference ccb->bsscount++; } //ccb_target_gen_emit("align %d", ast->decl.var->ctype->size < 8 ? ast->decl.var->ctype->size : 8); /* switch (ast->decl.var->ctype->size) { case 1: ccb_target_gen_emit("%s: db 0", ast->decl.var->variable.name); break; case 2: ccb_target_gen_emit("%s: dw 0", ast->decl.var->variable.name); break; case 4: ccb_target_gen_emit("%s: dd 0", ast->decl.var->variable.name); break; case 8: ccb_target_gen_emit("%s: dq 0", ast->decl.var->variable.name); break; default: ccb_target_gen_emit("%s:", ast->decl.var->variable.name); int i; for (i = 0; i < ast->decl.var->ctype->size; i++) { ccb_target_gen_emit(" db 0"); } } */ // TODO: Compiler seems to segfault instead of reporting error on a stray "*/" // Simply reserve the required number of bytes ccb_target_gen_emit("%s resb %d", ast->decl.var->variable.label, ast->decl.var->ctype->size); ccb->bsscount += ast->decl.var->ctype->size; } else { ccb_target_gen_emit(".lcomm %s, %d", ast->decl.var->variable.label, ast->decl.var->ctype->size); } } static void ccb_target_gen_global(ccb_t* ccb, ccb_ast_t* var) { if (var->decl.init) { ccb_target_gen_data(ccb, var); } else { ccb_target_gen_bss(ccb, var); } } void ccb_target_gen_data_section(ccb_t* ccb) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit_inline("section data"); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [data section]"); } else { ccb_target_gen_emit_inline("format elf64"); /*ccb_target_gen_emit("extrn printf ; XXX TODO Total hack"); ccb_target_gen_emit("extrn exit ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strcmp ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strlen ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strcat ; XXX TODO Total hack"); ccb_target_gen_emit("extrn calloc ; XXX TODO Total hack");*/ ccb_target_gen_emit_inline("section '.data' writeable"); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [data section]"); } else { /*ccb_target_gen_emit_inline("format elf64"); ccb_target_gen_emit("extrn printf ; XXX TODO Total hack"); ccb_target_gen_emit("extrn exit ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strcmp ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strlen ; XXX TODO Total hack"); ccb_target_gen_emit("extrn strcat ; XXX TODO Total hack"); ccb_target_gen_emit("extrn calloc ; XXX TODO Total hack");*/ /* Apparently it's necessary to address things using [rel ...] to get position-independent code on NASM. * Adding "default rel" at the start automates this, and allows the NASM backend to pass more tests. * However, I'm not sure if this is the correct solution, and doesn't fix all cases, so this remains a TODO. * The compiler build seems more-broken with it disabled. */ ccb_target_gen_emit_inline("default rel"); ccb_target_gen_emit_inline("section .data"); } } else { ccb_target_gen_emit_inline(".data"); } for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_ast_strings); !ccb_list_iterator_end(it); ) { ccb_ast_t* ast = ccb_list_iterator_next(it); ccb_target_gen_emit_inline("%s: ", ast->string.label); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit("data8 \"%s\", 0", ccb_string_quote_fasm(ast->string.data, '\"')); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { ccb_target_gen_emit("db '%s', 0", ccb_string_quote_fasm(ast->string.data, '\'')); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { ccb_target_gen_emit("db \"%s\", 0", ccb_string_quote_fasm(ast->string.data, '\"')); } else { ccb_target_gen_emit(".string \"%s\"", ccb_string_quote(ast->string.data)); } } for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_ast_floats); !ccb_list_iterator_end(it); ) { ccb_ast_t* ast = ccb_list_iterator_next(it); char* label = ccb_ast_label(ccb); ast->floating.label = label; ccb_target_gen_emit_inline("%s:", label); double tmp = ast->floating.value; if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data32 %d", ((int*)&tmp)[0]); ccb_target_gen_emit("data32 %d", ((int*)&tmp)[1]); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("dd %d", ((int*)&tmp)[0]); ccb_target_gen_emit("dd %d", ((int*)&tmp)[1]); } else { ccb_target_gen_emit(".long %d", ((int*)&tmp)[0]); ccb_target_gen_emit(".long %d", ((int*)&tmp)[1]); } } } static int ccb_target_gen_alignment(ccb_t* ccb, int n, int align) { int remainder = n % align; return (remainder == 0) ? n : n - remainder + align; } static void ccb_target_gen_function_prologue(ccb_t* ccb, ccb_ast_t* ast) { int callconv = 0; if (ccb_list_length(ast->function.params) > ccb_target_callregisters(ccb)) { // TODO: Track callconv if (ast->function.callconv != 0) { // fprintf(stderr, "NOTE: This function has calling convention %d\n", ast->function.callconv); //} //ccb_compile_error(ccb, "Too many params for function"); ccb_compile_warn(ccb, "This function will use stack arguments, which are only partly tested"); } if (ast->ctype->callconv != 0) { ccb_compile_warn(ccb, "Generating function with non-standard calling convention %d", ast->ctype->callconv); callconv = ast->ctype->callconv; } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_target_gen_emit_inline("section code"); ccb_target_gen_emit_inline("symbol %s, public", ast->function.name); } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [text section]"); ccb_target_gen_emit_inline("; [public %s]", ast->function.name); } else { ccb_target_gen_emit_inline("section '.text' executable"); // This is now handled by the predeclaration/extern-checking mechanism: //ccb_target_gen_emit_inline("public %s", ast->function.name); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [text section]"); ccb_target_gen_emit_inline("; [public %s]", ast->function.name); } else { ccb_target_gen_emit_inline("section .text"); // This is now handled by the predeclaration/extern-checking mechanism: //ccb_target_gen_emit_inline("global %s", ast->function.name); } } else { ccb_target_gen_emit_inline(".text"); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit_inline(".option norvc"); } ccb_target_gen_emit_inline(".global %s", ast->function.name); } ccb_target_gen_emit_inline("%s:", ast->function.name); if (ast->ctype->isnaked) { ccb_compile_warn(ccb, "Producing naked function with no prologue code!"); return; // Don't generate any more cruft if it's a naked function. } ccb_target_gen_stack += (ccb_target_wordsize(ccb)/8); // Remember to account for return instruction pointer as well as rbp if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_push("$rlink"); // On this architecture we need to store the return address explicitly } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_push("ra"); // On this architecture we need to store the return address explicitly } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0x55 ; push rbp"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { // We use an optimised approach for ARM (this should push both) ccb_target_gen_emit("stp x29, x30, [sp, #-32]"); //ccb_target_gen_emit("sub sp, sp, 32"); ccb_target_gen_stack -= (ccb_target_wordsize(ccb)/8); } else { ccb_target_gen_push(ccb_target_bp(ccb)); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("mov x29, sp"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi fp, sp, 0"); ccb_target_gen_emit("addi x8, sp, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("addimm $rbase, $rstack, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r5, r4"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rbp, rsp"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0x48, 0x89, 0xEC ; mov rbp, rsp"); } else { ccb_target_gen_emit("mov %%rsp, %%rbp"); } int offset = 0; int spareoffset = ccb_target_type_size_pointer(ccb)*2; int regi = 0; int regx = 0; /* At least for "__classic_call", if the return type won't naturally fit in the return registers then * a pointer to an area reserved for the result will have been inserted beneath the first regular argument. * So we just need to increment the spare arguments offset by one word in that case! * TODO: Store the offset of that argument somewhere to make sure other code doesn't get it wrong? */ if (callconv == 101 && ast->ctype->returntype->size > ccb_target_wordbytes(ccb)*2) { ccb_compile_warn(ccb, "Producing function with experimental large return value support!"); spareoffset += ccb_target_wordbytes(ccb); } for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.params); !ccb_list_iterator_end(it); ) { ccb_ast_t* value = ccb_list_iterator_next(it); bool spare = false; if (callconv != 101 && value->ctype->type == CCB_TYPE_FLOAT) { ccb_target_gen_push_xmm(regx++); } else if (callconv != 101 && (value->ctype->type == CCB_TYPE_DOUBLE || value->ctype->type == CCB_TYPE_LDOUBLE)) { ccb_target_gen_push_xmm(regx++); } else { // TODO: Support for 64-bit values on 32-bit architectures if (callconv != 101 && regi < ccb_target_callregisters(ccb)) { ccb_target_gen_push(ccb_target_callregister(ccb, regi++)); } else { spare = true; } } if (spare){ value->variable.off = spareoffset; spareoffset += ccb_target_gen_alignment(ccb, value->ctype->size, 8); } else { offset -= ccb_target_gen_alignment(ccb, value->ctype->size, 8); value->variable.off = offset; } } int localdata = 0; for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.locals); !ccb_list_iterator_end(it); ) { ccb_ast_t* value = ccb_list_iterator_next(it); int inneroffset = ccb_target_gen_alignment(ccb, value->ctype->size, 8); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("; var offset %d size %d <- offset %d ldata %d", inneroffset, value->ctype->size, offset, localdata); } else { ccb_target_gen_emit("# var offset %d size %d <- offset %d ldata %d", inneroffset, value->ctype->size, offset, localdata); } offset -= inneroffset; value->variable.off = offset; localdata -= inneroffset; if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("; var offset %d size %d -> offset %d ldata %d", inneroffset, value->ctype->size, offset, localdata); } else { ccb_target_gen_emit("# var offset %d size %d -> offset %d ldata %d", inneroffset, value->ctype->size, offset, localdata); } } /*while ((localdata % 16) != 0) { localdata--; }*/ if (localdata) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_emit("addiu sp, %d", localdata); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("subrc r4, %d", -localdata); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { //ccb_target_gen_emit("addi sp, sp, %d", localdata); ccb_target_gen_rv_addi(ccb, "sp", "sp", localdata); } else if ((ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("sub rsp, %d", -localdata); } else { ccb_target_gen_emit("sub $%d, %%rsp", -localdata); } //ccb_target_gen_stack += -localdata; // XXX TODO: I don't know *why* this fixes most of the test cases, it just does. -Zak. } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("; stack before: %d", ccb_target_gen_stack); } else { ccb_target_gen_emit("# stack before: %d", ccb_target_gen_stack); } ccb_target_gen_stack += -localdata;//-(offset - 8); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("; stack after: %d", ccb_target_gen_stack); } else { ccb_target_gen_emit("# stack after: %d", ccb_target_gen_stack); } } static void ccb_target_gen_function_epilogue(ccb_t* ccb) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_pop("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("li a0, 0"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("TODO"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { ccb_target_gen_emit("mov rax, 0"); } else { ccb_target_gen_emit("mov $0, %%rax"); } if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1) { ccb_target_gen_pop("$rbase"); ccb_target_gen_pop("$rlink"); ccb_target_gen_emit("bto $rlink, $rlink, $rlink"); /*ccb_target_gen_emit("addimm $sp, $fp, 8"); ccb_target_gen_emit("move sp, fp"); ccb_target_gen_emit("loadw fp, sp, 4"); ccb_target_gen_emit("addiu sp, sp, 8"); ccb_target_gen_emit("jr ra"); ccb_target_gen_emit("nop");*/ } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC) { ccb_target_gen_emit("setrr r4, r5"); ccb_target_gen_emit("popr r5"); ccb_target_gen_emit("return"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_RISCV) { ccb_target_gen_emit("addi sp, x8, 0"); ccb_target_gen_pop(ccb_target_bp(ccb)); ccb_target_gen_pop("ra"); ccb_target_gen_emit("ret"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_ARM) { ccb_target_gen_emit("ldp x29, x30, [sp], #32"); //ccb_target_gen_emit("add sp, sp, 16"); ccb_target_gen_emit("ret"); } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW) { ccb_target_gen_emit("data8 0xC9 ; leave"); ccb_target_gen_emit("data8 0xC3 ; ret"); } else { ccb_target_gen_emit("leave"); ccb_target_gen_emit("ret"); } } /* This is called to predaclare any functions as globals if needed. The purpose is mostly for * assemblers which require externs to be declared: If a symbol isn't declared then it should * be externed. * * TODO: Extra care may be needed for extern variables, and for other specialised linkage cases (especially on Windows). */ void ccb_target_gen_declfunction(ccb_t* ccb, ccb_ast_t* ast) { ccb_ast_setpos(ccb, ast); if (ast->type == CCB_AST_TYPE_FUNCTION) { if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [text section]"); ccb_target_gen_emit_inline("; [public %s]", ast->function.name); } else { //ccb_target_gen_emit_inline("section '.text' executable"); ccb_target_gen_emit_inline("public %s", ast->function.name); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [text section]"); ccb_target_gen_emit_inline("; [public %s]", ast->function.name); } else { //ccb_target_gen_emit_inline("section .text"); ccb_target_gen_emit_inline("global %s", ast->function.name); } } /* Set it as predeclared in the list. */ ccb_extrastage_setpredeclared(ccb, ast->function.name); } else if (ast->type == CCB_AST_TYPE_DECLARATION) { // Should we do any extra checks here?ccb_target_gen_global(ccb, ast); } else { ccb_compile_error(ccb, "ICE"); } } /* This is used to predeclare any explicit "extern" symbols. */ void ccb_target_gen_declextern(ccb_t* ccb, const char* name) { /* If something is declared as extern and then declared again, we skip the extern declaration. */ if (ccb_extrastage_ispredeclared(ccb, name)) { return; } if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [text section]"); ccb_target_gen_emit_inline("; [extrn %s]", name); } else { ccb_target_gen_emit_inline("extrn %s", name); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { ccb_target_gen_emit_inline("; [extern %s]", name); } else { ccb_target_gen_emit_inline("extern %s", name); } } /* Set it as predeclared in the list. */ ccb_extrastage_setpredeclared(ccb, name); } void ccb_target_gen_function(ccb_t* ccb, ccb_ast_t* ast) { ccb_ast_setpos(ccb, ast); ccb_target_gen_stack = 0; if (ast->type == CCB_AST_TYPE_FUNCTION) { //printf("Producing function '%s'...\n", ast->function.name); /*if (!ast->function.isnaked)*/ ccb_target_gen_function_prologue(ccb, ast); ccb_target_gen_expression(ccb, ast->function.body); if (!ast->ctype->isnaked) ccb_target_gen_function_epilogue(ccb); } else if (ast->type == CCB_AST_TYPE_DECLARATION) { ccb_target_gen_global(ccb, ast); } else { ccb_compile_error(ccb, "ICE"); } if (ccb_target_gen_stack > 8) { if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM || ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM)) { fprintf(ccb->output, ";; stack is misaligned by %d bytes [this warning may be outdated]\n", ccb_target_gen_stack); } else { fprintf(ccb->output, "## stack is misaligned by %d bytes [this warning may be outdated]\n", ccb_target_gen_stack); } } } /* From ifdef of CCBGENERIC_IMPLEMENTATION: */ #endif /* From ifndef at top of file: */ #endif