2020-10-07 14:11:16 +03:00
|
|
|
#include "chibicc.h"
|
|
|
|
|
2020-05-08 14:44:25 +03:00
|
|
|
static FILE *output_file;
|
2020-10-07 14:11:16 +03:00
|
|
|
static int depth;
|
2020-08-27 15:04:17 +03:00
|
|
|
static char *argreg8[] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"};
|
2020-09-06 02:10:01 +03:00
|
|
|
static char *argreg16[] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"};
|
2020-09-06 02:09:09 +03:00
|
|
|
static char *argreg32[] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"};
|
2020-08-27 15:04:17 +03:00
|
|
|
static char *argreg64[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
|
2020-09-04 11:58:53 +03:00
|
|
|
static Obj *current_fn;
|
2020-10-07 14:11:16 +03:00
|
|
|
|
2019-08-05 15:12:44 +03:00
|
|
|
static void gen_expr(Node *node);
|
2019-08-07 02:05:18 +03:00
|
|
|
static void gen_stmt(Node *node);
|
2019-08-05 15:12:44 +03:00
|
|
|
|
2020-09-03 16:00:02 +03:00
|
|
|
static void println(char *fmt, ...) {
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2020-05-08 14:44:25 +03:00
|
|
|
vfprintf(output_file, fmt, ap);
|
2020-09-03 16:00:02 +03:00
|
|
|
va_end(ap);
|
2020-05-08 14:44:25 +03:00
|
|
|
fprintf(output_file, "\n");
|
2020-09-03 16:00:02 +03:00
|
|
|
}
|
|
|
|
|
2020-10-07 06:47:09 +03:00
|
|
|
static int count(void) {
|
|
|
|
static int i = 1;
|
|
|
|
return i++;
|
|
|
|
}
|
|
|
|
|
2020-10-07 14:11:16 +03:00
|
|
|
static void push(void) {
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" push %%rax");
|
2020-10-07 14:11:16 +03:00
|
|
|
depth++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pop(char *arg) {
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" pop %s", arg);
|
2020-10-07 14:11:16 +03:00
|
|
|
depth--;
|
|
|
|
}
|
|
|
|
|
2020-10-07 14:12:19 +03:00
|
|
|
// Round up `n` to the nearest multiple of `align`. For instance,
|
|
|
|
// align_to(5, 8) returns 8 and align_to(11, 8) returns 16.
|
2020-08-30 11:21:54 +03:00
|
|
|
int align_to(int n, int align) {
|
2020-10-07 14:12:19 +03:00
|
|
|
return (n + align - 1) / align * align;
|
|
|
|
}
|
|
|
|
|
2020-09-26 02:59:56 +03:00
|
|
|
// Compute the absolute address of a given node.
|
|
|
|
// It's an error if a given node does not reside in memory.
|
|
|
|
static void gen_addr(Node *node) {
|
2019-08-05 15:12:44 +03:00
|
|
|
switch (node->kind) {
|
|
|
|
case ND_VAR:
|
2020-09-05 02:43:21 +03:00
|
|
|
if (node->var->is_local) {
|
|
|
|
// Local variable
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" lea %d(%%rbp), %%rax", node->var->offset);
|
2020-09-05 02:43:21 +03:00
|
|
|
} else {
|
|
|
|
// Global variable
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" lea %s(%%rip), %%rax", node->var->name);
|
2020-09-05 02:43:21 +03:00
|
|
|
}
|
2020-09-26 02:59:56 +03:00
|
|
|
return;
|
2019-08-05 15:12:44 +03:00
|
|
|
case ND_DEREF:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
return;
|
2019-08-12 04:29:17 +03:00
|
|
|
case ND_COMMA:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
gen_addr(node->rhs);
|
|
|
|
return;
|
2019-08-08 16:43:58 +03:00
|
|
|
case ND_MEMBER:
|
|
|
|
gen_addr(node->lhs);
|
|
|
|
println(" add $%d, %%rax", node->member->offset);
|
|
|
|
return;
|
2020-09-26 02:59:56 +03:00
|
|
|
}
|
|
|
|
|
2020-09-26 05:23:04 +03:00
|
|
|
error_tok(node->tok, "not an lvalue");
|
2020-09-26 02:59:56 +03:00
|
|
|
}
|
|
|
|
|
2020-09-26 04:15:32 +03:00
|
|
|
// Load a value from where %rax is pointing to.
|
|
|
|
static void load(Type *ty) {
|
2020-04-17 19:05:18 +03:00
|
|
|
if (ty->kind == TY_ARRAY || ty->kind == TY_STRUCT || ty->kind == TY_UNION) {
|
2020-09-26 04:15:32 +03:00
|
|
|
// If it is an array, do not attempt to load a value to the
|
|
|
|
// register because in general we can't load an entire array to a
|
|
|
|
// register. As a result, the result of an evaluation of an array
|
|
|
|
// becomes not the array itself but the address of the array.
|
|
|
|
// This is where "array is automatically converted to a pointer to
|
|
|
|
// the first element of the array in C" occurs.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-28 17:29:49 +03:00
|
|
|
char *insn = ty->is_unsigned ? "movz" : "movs";
|
|
|
|
|
2020-09-26 04:24:45 +03:00
|
|
|
// When we load a char or a short value to a register, we always
|
|
|
|
// extend them to the size of int, so we can assume the lower half of
|
|
|
|
// a register always contains a valid value. The upper half of a
|
|
|
|
// register for char, short and int may contain garbage. When we load
|
|
|
|
// a long value to a register, it simply occupies the entire register.
|
2020-08-27 15:04:17 +03:00
|
|
|
if (ty->size == 1)
|
2020-08-28 17:29:49 +03:00
|
|
|
println(" %sbl (%%rax), %%eax", insn);
|
2020-09-06 02:10:01 +03:00
|
|
|
else if (ty->size == 2)
|
2020-08-28 17:29:49 +03:00
|
|
|
println(" %swl (%%rax), %%eax", insn);
|
2020-09-06 02:09:09 +03:00
|
|
|
else if (ty->size == 4)
|
|
|
|
println(" movsxd (%%rax), %%rax");
|
2020-08-27 15:04:17 +03:00
|
|
|
else
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" mov (%%rax), %%rax");
|
2020-09-26 04:15:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store %rax to an address that the stack top is pointing to.
|
2020-08-27 15:04:17 +03:00
|
|
|
static void store(Type *ty) {
|
2020-09-26 04:15:32 +03:00
|
|
|
pop("%rdi");
|
2020-08-27 15:04:17 +03:00
|
|
|
|
2020-04-17 19:05:18 +03:00
|
|
|
if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) {
|
|
|
|
for (int i = 0; i < ty->size; i++) {
|
|
|
|
println(" mov %d(%%rax), %%r8b", i);
|
|
|
|
println(" mov %%r8b, %d(%%rdi)", i);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-27 15:04:17 +03:00
|
|
|
if (ty->size == 1)
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" mov %%al, (%%rdi)");
|
2020-09-06 02:10:01 +03:00
|
|
|
else if (ty->size == 2)
|
|
|
|
println(" mov %%ax, (%%rdi)");
|
2020-09-06 02:09:09 +03:00
|
|
|
else if (ty->size == 4)
|
|
|
|
println(" mov %%eax, (%%rdi)");
|
2020-08-27 15:04:17 +03:00
|
|
|
else
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" mov %%rax, (%%rdi)");
|
2020-09-26 04:15:32 +03:00
|
|
|
}
|
|
|
|
|
2020-08-28 16:07:54 +03:00
|
|
|
static void cmp_zero(Type *ty) {
|
|
|
|
if (is_integer(ty) && ty->size <= 4)
|
|
|
|
println(" cmp $0, %%eax");
|
|
|
|
else
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
}
|
|
|
|
|
2020-08-28 17:29:49 +03:00
|
|
|
enum { I8, I16, I32, I64, U8, U16, U32, U64 };
|
2019-08-11 10:06:14 +03:00
|
|
|
|
|
|
|
static int getTypeId(Type *ty) {
|
|
|
|
switch (ty->kind) {
|
|
|
|
case TY_CHAR:
|
2020-08-28 17:29:49 +03:00
|
|
|
return ty->is_unsigned ? U8 : I8;
|
2019-08-11 10:06:14 +03:00
|
|
|
case TY_SHORT:
|
2020-08-28 17:29:49 +03:00
|
|
|
return ty->is_unsigned ? U16 : I16;
|
2019-08-11 10:06:14 +03:00
|
|
|
case TY_INT:
|
2020-08-28 17:29:49 +03:00
|
|
|
return ty->is_unsigned ? U32 : I32;
|
|
|
|
case TY_LONG:
|
|
|
|
return ty->is_unsigned ? U64 : I64;
|
2019-08-11 10:06:14 +03:00
|
|
|
}
|
2020-08-28 17:29:49 +03:00
|
|
|
return U64;
|
2019-08-11 10:06:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// The table for type casts
|
|
|
|
static char i32i8[] = "movsbl %al, %eax";
|
2020-08-28 17:29:49 +03:00
|
|
|
static char i32u8[] = "movzbl %al, %eax";
|
2019-08-11 10:06:14 +03:00
|
|
|
static char i32i16[] = "movswl %ax, %eax";
|
2020-08-28 17:29:49 +03:00
|
|
|
static char i32u16[] = "movzwl %ax, %eax";
|
2019-08-11 10:06:14 +03:00
|
|
|
static char i32i64[] = "movsxd %eax, %rax";
|
2020-08-28 17:29:49 +03:00
|
|
|
static char u32i64[] = "mov %eax, %eax";
|
2019-08-11 10:06:14 +03:00
|
|
|
|
|
|
|
static char *cast_table[][10] = {
|
2020-08-28 17:29:49 +03:00
|
|
|
// i8 i16 i32 i64 u8 u16 u32 u64
|
|
|
|
{NULL, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64}, // i8
|
|
|
|
{i32i8, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64}, // i16
|
|
|
|
{i32i8, i32i16, NULL, i32i64, i32u8, i32u16, NULL, i32i64}, // i32
|
|
|
|
{i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL}, // i64
|
|
|
|
{i32i8, NULL, NULL, i32i64, NULL, NULL, NULL, i32i64}, // u8
|
|
|
|
{i32i8, i32i16, NULL, i32i64, i32u8, NULL, NULL, i32i64}, // u16
|
|
|
|
{i32i8, i32i16, NULL, u32i64, i32u8, i32u16, NULL, u32i64}, // u32
|
|
|
|
{i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL}, // u64
|
2019-08-11 10:06:14 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static void cast(Type *from, Type *to) {
|
|
|
|
if (to->kind == TY_VOID)
|
|
|
|
return;
|
|
|
|
|
2020-08-28 16:07:54 +03:00
|
|
|
if (to->kind == TY_BOOL) {
|
|
|
|
cmp_zero(from);
|
|
|
|
println(" setne %%al");
|
|
|
|
println(" movzx %%al, %%eax");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-11 10:06:14 +03:00
|
|
|
int t1 = getTypeId(from);
|
|
|
|
int t2 = getTypeId(to);
|
|
|
|
if (cast_table[t1][t2])
|
|
|
|
println(" %s", cast_table[t1][t2]);
|
|
|
|
}
|
|
|
|
|
2020-09-26 02:59:56 +03:00
|
|
|
// Generate code for a given node.
|
2020-10-07 14:11:16 +03:00
|
|
|
static void gen_expr(Node *node) {
|
2020-04-20 16:15:09 +03:00
|
|
|
println(" .loc 1 %d", node->tok->line_no);
|
|
|
|
|
2020-10-07 14:11:16 +03:00
|
|
|
switch (node->kind) {
|
2019-08-18 06:01:02 +03:00
|
|
|
case ND_NULL_EXPR:
|
|
|
|
return;
|
2020-09-27 13:43:03 +03:00
|
|
|
case ND_NUM: {
|
|
|
|
union { float f32; double f64; uint32_t u32; uint64_t u64; } u;
|
|
|
|
|
|
|
|
switch (node->ty->kind) {
|
|
|
|
case TY_FLOAT:
|
|
|
|
u.f32 = node->fval;
|
|
|
|
println(" mov $%u, %%eax # float %f", u.u32, node->fval);
|
|
|
|
println(" movq %%rax, %%xmm0");
|
|
|
|
return;
|
|
|
|
case TY_DOUBLE:
|
|
|
|
u.f64 = node->fval;
|
|
|
|
println(" mov $%lu, %%rax # double %f", u.u64, node->fval);
|
|
|
|
println(" movq %%rax, %%xmm0");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-27 15:51:00 +03:00
|
|
|
println(" mov $%ld, %%rax", node->val);
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
2020-09-27 13:43:03 +03:00
|
|
|
}
|
2020-10-07 14:11:16 +03:00
|
|
|
case ND_NEG:
|
|
|
|
gen_expr(node->lhs);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" neg %%rax");
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
2020-09-26 02:59:56 +03:00
|
|
|
case ND_VAR:
|
2019-08-08 16:43:58 +03:00
|
|
|
case ND_MEMBER:
|
2020-09-26 02:59:56 +03:00
|
|
|
gen_addr(node);
|
2020-09-26 04:15:32 +03:00
|
|
|
load(node->ty);
|
2020-09-26 02:59:56 +03:00
|
|
|
return;
|
2019-08-05 15:12:44 +03:00
|
|
|
case ND_DEREF:
|
|
|
|
gen_expr(node->lhs);
|
2020-09-26 04:15:32 +03:00
|
|
|
load(node->ty);
|
2019-08-05 15:12:44 +03:00
|
|
|
return;
|
|
|
|
case ND_ADDR:
|
|
|
|
gen_addr(node->lhs);
|
|
|
|
return;
|
2020-09-26 02:59:56 +03:00
|
|
|
case ND_ASSIGN:
|
|
|
|
gen_addr(node->lhs);
|
|
|
|
push();
|
|
|
|
gen_expr(node->rhs);
|
2020-08-27 15:04:17 +03:00
|
|
|
store(node->ty);
|
2020-09-26 02:59:56 +03:00
|
|
|
return;
|
2019-08-07 02:05:18 +03:00
|
|
|
case ND_STMT_EXPR:
|
|
|
|
for (Node *n = node->body; n; n = n->next)
|
|
|
|
gen_stmt(n);
|
|
|
|
return;
|
2019-08-12 04:29:17 +03:00
|
|
|
case ND_COMMA:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
gen_expr(node->rhs);
|
|
|
|
return;
|
2019-08-11 10:06:14 +03:00
|
|
|
case ND_CAST:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
cast(node->lhs->ty, node->ty);
|
|
|
|
return;
|
2020-09-18 07:36:43 +03:00
|
|
|
case ND_MEMZERO:
|
|
|
|
// `rep stosb` is equivalent to `memset(%rdi, %al, %rcx)`.
|
|
|
|
println(" mov $%d, %%rcx", node->var->ty->size);
|
|
|
|
println(" lea %d(%%rbp), %%rdi", node->var->offset);
|
|
|
|
println(" mov $0, %%al");
|
|
|
|
println(" rep stosb");
|
|
|
|
return;
|
2019-08-17 04:27:35 +03:00
|
|
|
case ND_COND: {
|
|
|
|
int c = count();
|
|
|
|
gen_expr(node->cond);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" je .L.else.%d", c);
|
|
|
|
gen_expr(node->then);
|
|
|
|
println(" jmp .L.end.%d", c);
|
|
|
|
println(".L.else.%d:", c);
|
|
|
|
gen_expr(node->els);
|
|
|
|
println(".L.end.%d:", c);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-13 13:31:04 +03:00
|
|
|
case ND_NOT:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" sete %%al");
|
|
|
|
println(" movzx %%al, %%rax");
|
|
|
|
return;
|
2019-08-13 13:41:11 +03:00
|
|
|
case ND_BITNOT:
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
println(" not %%rax");
|
|
|
|
return;
|
2020-10-07 14:22:11 +03:00
|
|
|
case ND_LOGAND: {
|
|
|
|
int c = count();
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" je .L.false.%d", c);
|
|
|
|
gen_expr(node->rhs);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" je .L.false.%d", c);
|
|
|
|
println(" mov $1, %%rax");
|
|
|
|
println(" jmp .L.end.%d", c);
|
|
|
|
println(".L.false.%d:", c);
|
|
|
|
println(" mov $0, %%rax");
|
|
|
|
println(".L.end.%d:", c);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case ND_LOGOR: {
|
|
|
|
int c = count();
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" jne .L.true.%d", c);
|
|
|
|
gen_expr(node->rhs);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" jne .L.true.%d", c);
|
|
|
|
println(" mov $0, %%rax");
|
|
|
|
println(" jmp .L.end.%d", c);
|
|
|
|
println(".L.true.%d:", c);
|
|
|
|
println(" mov $1, %%rax");
|
|
|
|
println(".L.end.%d:", c);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-04 13:03:46 +03:00
|
|
|
case ND_FUNCALL: {
|
|
|
|
int nargs = 0;
|
|
|
|
for (Node *arg = node->args; arg; arg = arg->next) {
|
|
|
|
gen_expr(arg);
|
|
|
|
push();
|
|
|
|
nargs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = nargs - 1; i >= 0; i--)
|
2020-08-27 15:04:17 +03:00
|
|
|
pop(argreg64[i]);
|
2019-08-04 13:03:46 +03:00
|
|
|
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" mov $0, %%rax");
|
2020-09-13 10:25:34 +03:00
|
|
|
|
|
|
|
if (depth % 2 == 0) {
|
|
|
|
println(" call %s", node->funcname);
|
|
|
|
} else {
|
|
|
|
println(" sub $8, %%rsp");
|
|
|
|
println(" call %s", node->funcname);
|
|
|
|
println(" add $8, %%rsp");
|
|
|
|
}
|
2020-09-13 10:25:46 +03:00
|
|
|
|
|
|
|
// It looks like the most significant 48 or 56 bits in RAX may
|
|
|
|
// contain garbage if a function return type is short or bool/char,
|
|
|
|
// respectively. We clear the upper bits here.
|
|
|
|
switch (node->ty->kind) {
|
|
|
|
case TY_BOOL:
|
|
|
|
println(" movzx %%al, %%eax");
|
|
|
|
return;
|
|
|
|
case TY_CHAR:
|
2020-08-28 17:29:49 +03:00
|
|
|
if (node->ty->is_unsigned)
|
|
|
|
println(" movzbl %%al, %%eax");
|
|
|
|
else
|
|
|
|
println(" movsbl %%al, %%eax");
|
2020-09-13 10:25:46 +03:00
|
|
|
return;
|
|
|
|
case TY_SHORT:
|
2020-08-28 17:29:49 +03:00
|
|
|
if (node->ty->is_unsigned)
|
|
|
|
println(" movzwl %%ax, %%eax");
|
|
|
|
else
|
|
|
|
println(" movswl %%ax, %%eax");
|
2020-09-13 10:25:46 +03:00
|
|
|
return;
|
|
|
|
}
|
2019-08-04 12:25:20 +03:00
|
|
|
return;
|
2020-10-07 14:11:16 +03:00
|
|
|
}
|
2019-08-04 13:03:46 +03:00
|
|
|
}
|
2020-10-07 14:11:16 +03:00
|
|
|
|
|
|
|
gen_expr(node->rhs);
|
|
|
|
push();
|
|
|
|
gen_expr(node->lhs);
|
|
|
|
pop("%rdi");
|
|
|
|
|
2020-08-28 17:29:49 +03:00
|
|
|
char *ax, *di, *dx;
|
2020-08-28 09:44:40 +03:00
|
|
|
|
|
|
|
if (node->lhs->ty->kind == TY_LONG || node->lhs->ty->base) {
|
|
|
|
ax = "%rax";
|
|
|
|
di = "%rdi";
|
2020-08-28 17:29:49 +03:00
|
|
|
dx = "%rdx";
|
2020-08-28 09:44:40 +03:00
|
|
|
} else {
|
|
|
|
ax = "%eax";
|
|
|
|
di = "%edi";
|
2020-08-28 17:29:49 +03:00
|
|
|
dx = "%edx";
|
2020-08-28 09:44:40 +03:00
|
|
|
}
|
|
|
|
|
2020-10-07 14:11:16 +03:00
|
|
|
switch (node->kind) {
|
|
|
|
case ND_ADD:
|
2020-08-28 09:44:40 +03:00
|
|
|
println(" add %s, %s", di, ax);
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
|
|
|
case ND_SUB:
|
2020-08-28 09:44:40 +03:00
|
|
|
println(" sub %s, %s", di, ax);
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
|
|
|
case ND_MUL:
|
2020-08-28 09:44:40 +03:00
|
|
|
println(" imul %s, %s", di, ax);
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
|
|
|
case ND_DIV:
|
2020-10-07 14:18:57 +03:00
|
|
|
case ND_MOD:
|
2020-08-28 17:29:49 +03:00
|
|
|
if (node->ty->is_unsigned) {
|
|
|
|
println(" mov $0, %s", dx);
|
|
|
|
println(" div %s", di);
|
|
|
|
} else {
|
|
|
|
if (node->lhs->ty->size == 8)
|
|
|
|
println(" cqo");
|
|
|
|
else
|
|
|
|
println(" cdq");
|
|
|
|
println(" idiv %s", di);
|
|
|
|
}
|
2020-10-07 14:18:57 +03:00
|
|
|
|
|
|
|
if (node->kind == ND_MOD)
|
|
|
|
println(" mov %%rdx, %%rax");
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
2020-10-07 14:19:35 +03:00
|
|
|
case ND_BITAND:
|
|
|
|
println(" and %%rdi, %%rax");
|
|
|
|
return;
|
|
|
|
case ND_BITOR:
|
|
|
|
println(" or %%rdi, %%rax");
|
|
|
|
return;
|
|
|
|
case ND_BITXOR:
|
|
|
|
println(" xor %%rdi, %%rax");
|
|
|
|
return;
|
2020-10-07 14:11:16 +03:00
|
|
|
case ND_EQ:
|
|
|
|
case ND_NE:
|
|
|
|
case ND_LT:
|
|
|
|
case ND_LE:
|
2020-08-28 09:44:40 +03:00
|
|
|
println(" cmp %s, %s", di, ax);
|
2020-10-07 14:11:16 +03:00
|
|
|
|
2020-08-28 17:29:49 +03:00
|
|
|
if (node->kind == ND_EQ) {
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" sete %%al");
|
2020-08-28 17:29:49 +03:00
|
|
|
} else if (node->kind == ND_NE) {
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" setne %%al");
|
2020-08-28 17:29:49 +03:00
|
|
|
} else if (node->kind == ND_LT) {
|
|
|
|
if (node->lhs->ty->is_unsigned)
|
|
|
|
println(" setb %%al");
|
|
|
|
else
|
|
|
|
println(" setl %%al");
|
|
|
|
} else if (node->kind == ND_LE) {
|
|
|
|
if (node->lhs->ty->is_unsigned)
|
|
|
|
println(" setbe %%al");
|
|
|
|
else
|
|
|
|
println(" setle %%al");
|
|
|
|
}
|
2020-10-07 14:11:16 +03:00
|
|
|
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" movzb %%al, %%rax");
|
2020-10-07 14:11:16 +03:00
|
|
|
return;
|
2020-10-07 14:23:22 +03:00
|
|
|
case ND_SHL:
|
|
|
|
println(" mov %%rdi, %%rcx");
|
|
|
|
println(" shl %%cl, %s", ax);
|
|
|
|
return;
|
|
|
|
case ND_SHR:
|
|
|
|
println(" mov %%rdi, %%rcx");
|
2020-08-28 17:29:49 +03:00
|
|
|
if (node->lhs->ty->is_unsigned)
|
|
|
|
println(" shr %%cl, %s", ax);
|
2020-10-07 14:23:22 +03:00
|
|
|
else
|
|
|
|
println(" sar %%cl, %s", ax);
|
|
|
|
return;
|
2020-10-07 14:11:16 +03:00
|
|
|
}
|
|
|
|
|
2020-09-26 05:23:04 +03:00
|
|
|
error_tok(node->tok, "invalid expression");
|
2020-10-07 14:11:16 +03:00
|
|
|
}
|
|
|
|
|
2020-09-26 02:50:44 +03:00
|
|
|
static void gen_stmt(Node *node) {
|
2020-04-20 16:15:09 +03:00
|
|
|
println(" .loc 1 %d", node->tok->line_no);
|
|
|
|
|
2020-10-07 14:12:57 +03:00
|
|
|
switch (node->kind) {
|
2020-10-07 06:47:09 +03:00
|
|
|
case ND_IF: {
|
|
|
|
int c = count();
|
|
|
|
gen_expr(node->cond);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" je .L.else.%d", c);
|
2020-10-07 06:47:09 +03:00
|
|
|
gen_stmt(node->then);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" jmp .L.end.%d", c);
|
|
|
|
println(".L.else.%d:", c);
|
2020-10-07 06:47:09 +03:00
|
|
|
if (node->els)
|
|
|
|
gen_stmt(node->els);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(".L.end.%d:", c);
|
2020-10-07 06:47:09 +03:00
|
|
|
return;
|
|
|
|
}
|
2019-08-04 11:35:53 +03:00
|
|
|
case ND_FOR: {
|
|
|
|
int c = count();
|
2019-08-04 11:24:03 +03:00
|
|
|
if (node->init)
|
|
|
|
gen_stmt(node->init);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(".L.begin.%d:", c);
|
2019-08-04 11:35:53 +03:00
|
|
|
if (node->cond) {
|
|
|
|
gen_expr(node->cond);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" cmp $0, %%rax");
|
2019-08-15 07:48:41 +03:00
|
|
|
println(" je %s", node->brk_label);
|
2019-08-04 11:35:53 +03:00
|
|
|
}
|
|
|
|
gen_stmt(node->then);
|
2020-08-27 15:59:19 +03:00
|
|
|
println("%s:", node->cont_label);
|
2019-08-04 11:35:53 +03:00
|
|
|
if (node->inc)
|
|
|
|
gen_expr(node->inc);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" jmp .L.begin.%d", c);
|
2019-08-15 07:48:41 +03:00
|
|
|
println("%s:", node->brk_label);
|
2019-08-04 11:35:53 +03:00
|
|
|
return;
|
|
|
|
}
|
2019-08-24 10:09:46 +03:00
|
|
|
case ND_DO: {
|
|
|
|
int c = count();
|
|
|
|
println(".L.begin.%d:", c);
|
|
|
|
gen_stmt(node->then);
|
|
|
|
println("%s:", node->cont_label);
|
|
|
|
gen_expr(node->cond);
|
|
|
|
println(" cmp $0, %%rax");
|
|
|
|
println(" jne .L.begin.%d", c);
|
|
|
|
println("%s:", node->brk_label);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-15 10:43:24 +03:00
|
|
|
case ND_SWITCH:
|
|
|
|
gen_expr(node->cond);
|
|
|
|
|
|
|
|
for (Node *n = node->case_next; n; n = n->case_next) {
|
|
|
|
char *reg = (node->cond->ty->size == 8) ? "%rax" : "%eax";
|
|
|
|
println(" cmp $%ld, %s", n->val, reg);
|
|
|
|
println(" je %s", n->label);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->default_case)
|
|
|
|
println(" jmp %s", node->default_case->label);
|
|
|
|
|
|
|
|
println(" jmp %s", node->brk_label);
|
|
|
|
gen_stmt(node->then);
|
|
|
|
println("%s:", node->brk_label);
|
|
|
|
return;
|
|
|
|
case ND_CASE:
|
|
|
|
println("%s:", node->label);
|
|
|
|
gen_stmt(node->lhs);
|
|
|
|
return;
|
2020-09-04 07:38:41 +03:00
|
|
|
case ND_BLOCK:
|
|
|
|
for (Node *n = node->body; n; n = n->next)
|
|
|
|
gen_stmt(n);
|
|
|
|
return;
|
2020-09-04 06:27:21 +03:00
|
|
|
case ND_GOTO:
|
|
|
|
println(" jmp %s", node->unique_label);
|
|
|
|
return;
|
|
|
|
case ND_LABEL:
|
|
|
|
println("%s:", node->unique_label);
|
|
|
|
gen_stmt(node->lhs);
|
|
|
|
return;
|
2020-10-07 14:12:57 +03:00
|
|
|
case ND_RETURN:
|
2020-08-27 16:01:34 +03:00
|
|
|
if (node->lhs)
|
|
|
|
gen_expr(node->lhs);
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" jmp .L.return.%s", current_fn->name);
|
2020-10-07 14:12:57 +03:00
|
|
|
return;
|
|
|
|
case ND_EXPR_STMT:
|
2020-09-26 02:50:44 +03:00
|
|
|
gen_expr(node->lhs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-26 05:23:04 +03:00
|
|
|
error_tok(node->tok, "invalid statement");
|
2020-09-26 02:50:44 +03:00
|
|
|
}
|
|
|
|
|
2020-10-07 14:12:19 +03:00
|
|
|
// Assign offsets to local variables.
|
2020-09-04 11:58:53 +03:00
|
|
|
static void assign_lvar_offsets(Obj *prog) {
|
|
|
|
for (Obj *fn = prog; fn; fn = fn->next) {
|
|
|
|
if (!fn->is_function)
|
|
|
|
continue;
|
|
|
|
|
2020-09-04 13:01:33 +03:00
|
|
|
int offset = 0;
|
|
|
|
for (Obj *var = fn->locals; var; var = var->next) {
|
2020-09-26 04:15:32 +03:00
|
|
|
offset += var->ty->size;
|
2020-09-04 05:20:55 +03:00
|
|
|
offset = align_to(offset, var->align);
|
2020-09-04 13:01:33 +03:00
|
|
|
var->offset = -offset;
|
|
|
|
}
|
|
|
|
fn->stack_size = align_to(offset, 16);
|
2020-10-07 14:12:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-05 02:43:21 +03:00
|
|
|
static void emit_data(Obj *prog) {
|
|
|
|
for (Obj *var = prog; var; var = var->next) {
|
2020-09-04 11:27:22 +03:00
|
|
|
if (var->is_function || !var->is_definition)
|
2020-09-05 02:43:21 +03:00
|
|
|
continue;
|
2020-10-07 14:12:19 +03:00
|
|
|
|
2020-09-04 05:21:12 +03:00
|
|
|
if (var->is_static)
|
|
|
|
println(" .local %s", var->name);
|
|
|
|
else
|
|
|
|
println(" .globl %s", var->name);
|
|
|
|
|
2020-09-04 05:20:55 +03:00
|
|
|
println(" .align %d", var->align);
|
2020-10-07 06:49:08 +03:00
|
|
|
|
|
|
|
if (var->init_data) {
|
2020-08-19 11:15:03 +03:00
|
|
|
println(" .data");
|
|
|
|
println("%s:", var->name);
|
|
|
|
|
2020-07-20 17:53:12 +03:00
|
|
|
Relocation *rel = var->rel;
|
|
|
|
int pos = 0;
|
|
|
|
while (pos < var->ty->size) {
|
|
|
|
if (rel && rel->offset == pos) {
|
|
|
|
println(" .quad %s%+ld", rel->label, rel->addend);
|
|
|
|
rel = rel->next;
|
|
|
|
pos += 8;
|
|
|
|
} else {
|
|
|
|
println(" .byte %d", var->init_data[pos++]);
|
|
|
|
}
|
|
|
|
}
|
2020-08-19 11:15:03 +03:00
|
|
|
continue;
|
2020-10-07 06:49:08 +03:00
|
|
|
}
|
2020-08-19 11:15:03 +03:00
|
|
|
|
|
|
|
println(" .bss");
|
|
|
|
println("%s:", var->name);
|
|
|
|
println(" .zero %d", var->ty->size);
|
2020-09-05 02:43:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:09:09 +03:00
|
|
|
static void store_gp(int r, int offset, int sz) {
|
|
|
|
switch (sz) {
|
|
|
|
case 1:
|
|
|
|
println(" mov %s, %d(%%rbp)", argreg8[r], offset);
|
|
|
|
return;
|
2020-09-06 02:10:01 +03:00
|
|
|
case 2:
|
|
|
|
println(" mov %s, %d(%%rbp)", argreg16[r], offset);
|
|
|
|
return;
|
2020-09-06 02:09:09 +03:00
|
|
|
case 4:
|
|
|
|
println(" mov %s, %d(%%rbp)", argreg32[r], offset);
|
|
|
|
return;
|
|
|
|
case 8:
|
|
|
|
println(" mov %s, %d(%%rbp)", argreg64[r], offset);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unreachable();
|
|
|
|
}
|
|
|
|
|
2020-09-05 02:43:21 +03:00
|
|
|
static void emit_text(Obj *prog) {
|
2020-09-04 11:58:53 +03:00
|
|
|
for (Obj *fn = prog; fn; fn = fn->next) {
|
2020-09-04 05:45:29 +03:00
|
|
|
if (!fn->is_function || !fn->is_definition)
|
2020-09-04 11:58:53 +03:00
|
|
|
continue;
|
|
|
|
|
2020-09-04 11:25:15 +03:00
|
|
|
if (fn->is_static)
|
|
|
|
println(" .local %s", fn->name);
|
|
|
|
else
|
|
|
|
println(" .globl %s", fn->name);
|
|
|
|
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" .text");
|
|
|
|
println("%s:", fn->name);
|
2020-09-04 13:01:33 +03:00
|
|
|
current_fn = fn;
|
|
|
|
|
|
|
|
// Prologue
|
2020-09-03 16:00:02 +03:00
|
|
|
println(" push %%rbp");
|
|
|
|
println(" mov %%rsp, %%rbp");
|
|
|
|
println(" sub $%d, %%rsp", fn->stack_size);
|
2020-09-04 13:01:33 +03:00
|
|
|
|
2019-08-25 05:48:44 +03:00
|
|
|
// Save arg registers if function is variadic
|
|
|
|
if (fn->va_area) {
|
|
|
|
int gp = 0;
|
|
|
|
for (Obj *var = fn->params; var; var = var->next)
|
|
|
|
gp++;
|
|
|
|
int off = fn->va_area->offset;
|
|
|
|
|
|
|
|
// va_elem
|
|
|
|
println(" movl $%d, %d(%%rbp)", gp * 8, off);
|
|
|
|
println(" movl $0, %d(%%rbp)", off + 4);
|
|
|
|
println(" movq %%rbp, %d(%%rbp)", off + 16);
|
|
|
|
println(" addq $%d, %d(%%rbp)", off + 24, off + 16);
|
|
|
|
|
|
|
|
// __reg_save_area__
|
|
|
|
println(" movq %%rdi, %d(%%rbp)", off + 24);
|
|
|
|
println(" movq %%rsi, %d(%%rbp)", off + 32);
|
|
|
|
println(" movq %%rdx, %d(%%rbp)", off + 40);
|
|
|
|
println(" movq %%rcx, %d(%%rbp)", off + 48);
|
|
|
|
println(" movq %%r8, %d(%%rbp)", off + 56);
|
|
|
|
println(" movq %%r9, %d(%%rbp)", off + 64);
|
|
|
|
println(" movsd %%xmm0, %d(%%rbp)", off + 72);
|
|
|
|
println(" movsd %%xmm1, %d(%%rbp)", off + 80);
|
|
|
|
println(" movsd %%xmm2, %d(%%rbp)", off + 88);
|
|
|
|
println(" movsd %%xmm3, %d(%%rbp)", off + 96);
|
|
|
|
println(" movsd %%xmm4, %d(%%rbp)", off + 104);
|
|
|
|
println(" movsd %%xmm5, %d(%%rbp)", off + 112);
|
|
|
|
println(" movsd %%xmm6, %d(%%rbp)", off + 120);
|
|
|
|
println(" movsd %%xmm7, %d(%%rbp)", off + 128);
|
|
|
|
}
|
|
|
|
|
2020-09-04 07:39:48 +03:00
|
|
|
// Save passed-by-register arguments to the stack
|
|
|
|
int i = 0;
|
2020-09-06 02:09:09 +03:00
|
|
|
for (Obj *var = fn->params; var; var = var->next)
|
|
|
|
store_gp(i++, var->offset, var->ty->size);
|
2020-09-04 07:39:48 +03:00
|
|
|
|
2020-09-04 13:01:33 +03:00
|
|
|
// Emit code
|
|
|
|
gen_stmt(fn->body);
|
|
|
|
assert(depth == 0);
|
|
|
|
|
|
|
|
// Epilogue
|
2020-09-03 16:00:02 +03:00
|
|
|
println(".L.return.%s:", fn->name);
|
|
|
|
println(" mov %%rbp, %%rsp");
|
|
|
|
println(" pop %%rbp");
|
|
|
|
println(" ret");
|
2020-09-04 13:01:33 +03:00
|
|
|
}
|
2020-10-07 14:11:16 +03:00
|
|
|
}
|
2020-09-05 02:43:21 +03:00
|
|
|
|
2020-05-08 14:44:25 +03:00
|
|
|
void codegen(Obj *prog, FILE *out) {
|
|
|
|
output_file = out;
|
|
|
|
|
2020-09-05 02:43:21 +03:00
|
|
|
assign_lvar_offsets(prog);
|
|
|
|
emit_data(prog);
|
|
|
|
emit_text(prog);
|
|
|
|
}
|