slcc/ccbgeneric.h

4257 lines
185 KiB
C

#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