rusi/codegen.c

1568 lines
63 KiB
C
Raw Normal View History

#включить "руси.з"
// Максимальное количество аргументов в регистрах общего назначения и XMM
#определить GP_MAX 6
#определить FP_MAX 8
стат FILE *выходнойайл;
стат цел глубина_стека;
стат символ *арг_рег8[] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"};
стат символ *арг_рег16[] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"};
стат символ *арг_рег32[] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"};
стат символ *арг_рег64[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
стат Obj *текущаяункция;
стат пусто сгенерировать_выражение(Node *узел);
стат пусто сгенерировать_оператор(Node *узел);
// Вывод строки в выходной файл с форматированием
__attribute__((format(printf, 1, 2)))
стат пусто вывести_строку(символ *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(выходнойайл, fmt, args);
va_end(args);
fprintf(выходнойайл, "\n");
}
// Генератор уникальных номеров меток
стат цел сгенерироватьомеретки(пусто) {
стат цел счетчикеток = 1;
возврат счетчикеток++;
}
// Поместить значение из RAX в стек
стат пусто положить_в_стек(пусто) {
вывести_строку(" push %%rax");
глубина_стека++;
}
// Извлечь значение из стека в указанный регистр
стат пусто извлечь_из_стека(символ *рег) {
вывести_строку(" pop %s", рег);
глубина_стека--;
}
// Поместить значение из XMM0 в стек (для чисел с плавающей точкой)
стат пусто положитьробное_в_стек(пусто) {
вывести_строку(" sub $8, %%rsp");
вывести_строку(" movsd %%xmm0, (%%rsp)");
глубина_стека++;
}
// Извлечь значение из стека в XMM регистр
стат пусто извлечьробное_из_стека(цел номер_рег) {
вывести_строку(" movsd (%%rsp), %%xmm%d", номер_рег);
вывести_строку(" add $8, %%rsp");
глубина_стека--;
}
// Округление числа вверх до ближайшего кратного align
цел align_to(цел число, цел выравнивание) {
возврат (число + выравнивание - 1) / выравнивание * выравнивание;
}
// Получить имя регистра DX в зависимости от размера
стат символ *получить_рег_dx(цел разм) {
выбор (разм) {
случай 1: возврат "%dl";
случай 2: возврат "%dx";
случай 4: возврат "%edx";
случай 8: возврат "%rdx";
}
unreachable();
}
// Получить имя регистра AX в зависимости от размера
стат символ *получить_рег_ax(цел разм) {
выбор (разм) {
случай 1: возврат "%al";
случай 2: возврат "%ax";
случай 4: возврат "%eax";
случай 8: возврат "%rax";
}
unreachable();
}
// Генерация абсолютного адреса для узла
стат пусто сгенерировать_адрес(Node *узел) {
выбор (узел->kind) {
случай ND_VAR:
если (узел->var->ty->kind == TY_VLA) {
вывести_строку(" mov %d(%%rbp), %%rax", узел->var->offset);
возврат;
}
если (узел->var->is_local) {
вывести_строку(" lea %d(%%rbp), %%rax", узел->var->offset);
возврат;
}
если (opt_fpic) {
если (узел->var->is_tls) {
вывести_строку(" data16 lea %s@tlsgd(%%rip), %%rdi", узел->var->name);
вывести_строку(" .value 0x6666");
вывести_строку(" rex64");
вывести_строку(" call __tls_get_addr@PLT");
возврат;
}
вывести_строку(" mov %s@GOTPCREL(%%rip), %%rax", узел->var->name);
возврат;
}
если (узел->var->is_tls) {
вывести_строку(" mov %%fs:0, %%rax");
вывести_строку(" add $%s@tpoff, %%rax", узел->var->name);
возврат;
}
если (узел->ty->kind == TY_FUNC) {
если (узел->var->is_definition)
вывести_строку(" lea %s(%%rip), %%rax", узел->var->name);
иначе
вывести_строку(" mov %s@GOTPCREL(%%rip), %%rax", узел->var->name);
возврат;
}
вывести_строку(" lea %s(%%rip), %%rax", узел->var->name);
возврат;
случай ND_DEREF:
сгенерировать_выражение(узел->lhs);
возврат;
случай ND_COMMA:
сгенерировать_выражение(узел->lhs);
сгенерировать_адрес(узел->rhs);
возврат;
случай ND_MEMBER:
сгенерировать_адрес(узел->lhs);
вывести_строку(" add $%d, %%rax", узел->member->offset);
возврат;
случай ND_FUNCALL:
если (узел->ret_buffer) {
сгенерировать_выражение(узел);
возврат;
}
прерви;
случай ND_ASSIGN:
случай ND_COND:
если (узел->ty->kind == TY_STRUCT || узел->ty->kind == TY_UNION) {
сгенерировать_выражение(узел);
возврат;
}
прерви;
случай ND_VLA_PTR:
вывести_строку(" lea %d(%%rbp), %%rax", узел->var->offset);
возврат;
}
error_tok(узел->tok, "not an lvalue");
}
// Загрузка значения из памяти по адресу в RAX
стат пусто загрузить_значение(Type *тип) {
выбор (тип->kind) {
случай TY_ARRAY:
случай TY_STRUCT:
случай TY_UNION:
случай TY_FUNC:
случай TY_VLA:
возврат;
случай TY_FLOAT:
вывести_строку(" movss (%%rax), %%xmm0");
возврат;
случай TY_DOUBLE:
вывести_строку(" movsd (%%rax), %%xmm0");
возврат;
случай TY_LDOUBLE:
вывести_строку(" fldt (%%rax)");
возврат;
}
символ *инструкция = тип->is_unsigned ? "movz" : "movs";
если (тип->size == 1)
вывести_строку(" %sbl (%%rax), %%eax", инструкция);
иначе если (тип->size == 2)
вывести_строку(" %swl (%%rax), %%eax", инструкция);
иначе если (тип->size == 4)
вывести_строку(" movsxd (%%rax), %%rax");
иначе
вывести_строку(" mov (%%rax), %%rax");
}
// Сохранение значения из RAX по адресу на вершине стека
стат пусто сохранить_значение(Type *тип) {
извлечь_из_стека("%rdi");
выбор (тип->kind) {
случай TY_STRUCT:
случай TY_UNION:
для (цел i = 0; i < тип->size; i++) {
вывести_строку(" mov %d(%%rax), %%r8b", i);
вывести_строку(" mov %%r8b, %d(%%rdi)", i);
}
возврат;
случай TY_FLOAT:
вывести_строку(" movss %%xmm0, (%%rdi)");
возврат;
случай TY_DOUBLE:
вывести_строку(" movsd %%xmm0, (%%rdi)");
возврат;
случай TY_LDOUBLE:
вывести_строку(" fstpt (%%rdi)");
возврат;
}
если (тип->size == 1)
вывести_строку(" mov %%al, (%%rdi)");
иначе если (тип->size == 2)
вывести_строку(" mov %%ax, (%%rdi)");
иначе если (тип->size == 4)
вывести_строку(" mov %%eax, (%%rdi)");
иначе
вывести_строку(" mov %%rax, (%%rdi)");
}
// Сравнение значения с нулем
стат пусто сравнить_сулем(Type *тип) {
выбор (тип->kind) {
случай TY_FLOAT:
вывести_строку(" xorps %%xmm1, %%xmm1");
вывести_строку(" ucomiss %%xmm1, %%xmm0");
возврат;
случай TY_DOUBLE:
вывести_строку(" xorpd %%xmm1, %%xmm1");
вывести_строку(" ucomisd %%xmm1, %%xmm0");
возврат;
случай TY_LDOUBLE:
вывести_строку(" fldz");
вывести_строку(" fucomip");
вывести_строку(" fstp %%st(0)");
возврат;
}
если (is_integer(тип) && тип->size <= 4)
вывести_строку(" cmp $0, %%eax");
иначе
вывести_строку(" cmp $0, %%rax");
}
// Идентификаторы типов для таблицы преобразований
переч { I8, I16, I32, I64, U8, U16, U32, U64, F32, F64, F80 };
// Получение идентификатора типа
стат цел получить_ид_типа(Type *тип) {
выбор (тип->kind) {
случай TY_CHAR:
возврат тип->is_unsigned ? U8 : I8;
случай TY_SHORT:
возврат тип->is_unsigned ? U16 : I16;
случай TY_INT:
возврат тип->is_unsigned ? U32 : I32;
случай TY_LONG:
возврат тип->is_unsigned ? U64 : I64;
случай TY_FLOAT:
возврат F32;
случай TY_DOUBLE:
возврат F64;
случай TY_LDOUBLE:
возврат F80;
}
возврат U64;
}
// Таблица инструкций для преобразования типов
стат символ i32i8[] = "movsbl %al, %eax";
стат символ i32u8[] = "movzbl %al, %eax";
стат символ i32i16[] = "movswl %ax, %eax";
стат символ i32u16[] = "movzwl %ax, %eax";
стат символ i32f32[] = "cvtsi2ssl %eax, %xmm0";
стат символ i32i64[] = "movsxd %eax, %rax";
стат символ i32f64[] = "cvtsi2sdl %eax, %xmm0";
стат символ i32f80[] = "mov %eax, -4(%rsp); fildl -4(%rsp)";
стат символ u32f32[] = "mov %eax, %eax; cvtsi2ssq %rax, %xmm0";
стат символ u32i64[] = "mov %eax, %eax";
стат символ u32f64[] = "mov %eax, %eax; cvtsi2sdq %rax, %xmm0";
стат символ u32f80[] = "mov %eax, %eax; mov %rax, -8(%rsp); fildll -8(%rsp)";
стат символ i64f32[] = "cvtsi2ssq %rax, %xmm0";
стат символ i64f64[] = "cvtsi2sdq %rax, %xmm0";
стат символ i64f80[] = "movq %rax, -8(%rsp); fildll -8(%rsp)";
стат символ u64f32[] = "cvtsi2ssq %rax, %xmm0";
стат символ u64f64[] =
"test %rax,%rax; js 1f; pxor %xmm0,%xmm0; cvtsi2sd %rax,%xmm0; jmp 2f; "
"1: mov %rax,%rdi; and $1,%eax; pxor %xmm0,%xmm0; shr %rdi; "
"or %rax,%rdi; cvtsi2sd %rdi,%xmm0; addsd %xmm0,%xmm0; 2:";
стат символ u64f80[] =
"mov %rax, -8(%rsp); fildq -8(%rsp); test %rax, %rax; jns 1f;"
"mov $1602224128, %eax; mov %eax, -4(%rsp); fadds -4(%rsp); 1:";
стат символ f32i8[] = "cvttss2sil %xmm0, %eax; movsbl %al, %eax";
стат символ f32u8[] = "cvttss2sil %xmm0, %eax; movzbl %al, %eax";
стат символ f32i16[] = "cvttss2sil %xmm0, %eax; movswl %ax, %eax";
стат символ f32u16[] = "cvttss2sil %xmm0, %eax; movzwl %ax, %eax";
стат символ f32i32[] = "cvttss2sil %xmm0, %eax";
стат символ f32u32[] = "cvttss2siq %xmm0, %rax";
стат символ f32i64[] = "cvttss2siq %xmm0, %rax";
стат символ f32u64[] = "cvttss2siq %xmm0, %rax";
стат символ f32f64[] = "cvtss2sd %xmm0, %xmm0";
стат символ f32f80[] = "movss %xmm0, -4(%rsp); flds -4(%rsp)";
стат символ f64i8[] = "cvttsd2sil %xmm0, %eax; movsbl %al, %eax";
стат символ f64u8[] = "cvttsd2sil %xmm0, %eax; movzbl %al, %eax";
стат символ f64i16[] = "cvttsd2sil %xmm0, %eax; movswl %ax, %eax";
стат символ f64u16[] = "cvttsd2sil %xmm0, %eax; movzwl %ax, %eax";
стат символ f64i32[] = "cvttsd2sil %xmm0, %eax";
стат символ f64u32[] = "cvttsd2siq %xmm0, %rax";
стат символ f64i64[] = "cvttsd2siq %xmm0, %rax";
стат символ f64u64[] = "cvttsd2siq %xmm0, %rax";
стат символ f64f32[] = "cvtsd2ss %xmm0, %xmm0";
стат символ f64f80[] = "movsd %xmm0, -8(%rsp); fldl -8(%rsp)";
#определить FROM_F80_1 \
"fnstcw -10(%rsp); movzwl -10(%rsp), %eax; or $12, %ah; " \
"mov %ax, -12(%rsp); fldcw -12(%rsp); "
#определить FROM_F80_2 " -24(%rsp); fldcw -10(%rsp); "
стат символ f80i8[] = FROM_F80_1 "fistps" FROM_F80_2 "movsbl -24(%rsp), %eax";
стат символ f80u8[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax";
стат символ f80i16[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax";
стат символ f80u16[] = FROM_F80_1 "fistpl" FROM_F80_2 "movswl -24(%rsp), %eax";
стат символ f80i32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax";
стат символ f80u32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax";
стат символ f80i64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax";
стат символ f80u64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax";
стат символ f80f32[] = "fstps -8(%rsp); movss -8(%rsp), %xmm0";
стат символ f80f64[] = "fstpl -8(%rsp); movsd -8(%rsp), %xmm0";
// Таблица преобразований типов
стат символ *таблица_преобразований[][11] = {
{NULL, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80},
{i32i8, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80},
{i32i8, i32i16, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80},
{i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, i64f32, i64f64, i64f80},
{i32i8, NULL, NULL, i32i64, NULL, NULL, NULL, i32i64, i32f32, i32f64, i32f80},
{i32i8, i32i16, NULL, i32i64, i32u8, NULL, NULL, i32i64, i32f32, i32f64, i32f80},
{i32i8, i32i16, NULL, u32i64, i32u8, i32u16, NULL, u32i64, u32f32, u32f64, u32f80},
{i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, u64f32, u64f64, u64f80},
{f32i8, f32i16, f32i32, f32i64, f32u8, f32u16, f32u32, f32u64, NULL, f32f64, f32f80},
{f64i8, f64i16, f64i32, f64i64, f64u8, f64u16, f64u32, f64u64, f64f32, NULL, f64f80},
{f80i8, f80i16, f80i32, f80i64, f80u8, f80u16, f80u32, f80u64, f80f32, f80f64, NULL},
};
// Генерация кода для преобразования типа
стат пусто сгенерировать_приведение_типа(Type *от_типа, Type *к_типу) {
если (к_типу->kind == TY_VOID)
возврат;
если (к_типу->kind == TY_BOOL) {
сравнить_сулем(от_типа);
вывести_строку(" setne %%al");
вывести_строку(" movzx %%al, %%eax");
возврат;
}
цел идсточника = получить_ид_типа(от_типа);
цел идазначения = получить_ид_типа(к_типу);
если (таблица_преобразований[идсточника][идазначения])
вывести_строку(" %s", таблица_преобразований[идсточника][идазначения]);
}
// Проверка, содержит ли структура только вещественные члены в заданном диапазоне байт
стат бул толькоробные_члены(Type *тип, цел начало, цел конец, цел смещение) {
если (тип->kind == TY_STRUCT || тип->kind == TY_UNION) {
для (Member *член = тип->members; член; член = член->next)
если (!толькоробные_члены(член->ty, начало, конец, смещение + член->offset))
возврат ложь;
возврат истина;
}
если (тип->kind == TY_ARRAY) {
для (цел i = 0; i < тип->array_len; i++)
если (!толькоробные_члены(тип->base, начало, конец, смещение + тип->base->size * i))
возврат ложь;
возврат истина;
}
возврат смещение < начало || конец <= смещение || тип->kind == TY_FLOAT || тип->kind == TY_DOUBLE;
}
стат бул первые_8_байт_дробные(Type *тип) {
возврат толькоробные_члены(тип, 0, 8, 0);
}
стат бул вторые_8_байт_дробные(Type *тип) {
возврат толькоробные_члены(тип, 8, 16, 0);
}
// Поместить структуру в стек
стат пусто положить_структуру_в_стек(Type *тип) {
цел выровненный_разм = align_to(тип->size, 8);
вывести_строку(" sub $%d, %%rsp", выровненный_разм);
глубина_стека += выровненный_разм / 8;
для (цел i = 0; i < тип->size; i++) {
вывести_строку(" mov %d(%%rax), %%r10b", i);
вывести_строку(" mov %%r10b, %d(%%rsp)", i);
}
}
// Рекурсивная обработка аргументов функции
стат пусто обработать_аргументы_функции(Node *арг, бул первый_проход) {
если (!арг)
возврат;
обработать_аргументы_функции(арг->next, первый_проход);
если ((первый_проход && !арг->pass_by_stack) || (!первый_проход && арг->pass_by_stack))
возврат;
сгенерировать_выражение(арг);
выбор (арг->ty->kind) {
случай TY_STRUCT:
случай TY_UNION:
положить_структуру_в_стек(арг->ty);
прерви;
случай TY_FLOAT:
случай TY_DOUBLE:
положитьробное_в_стек();
прерви;
случай TY_LDOUBLE:
вывести_строку(" sub $16, %%rsp");
вывести_строку(" fstpt (%%rsp)");
глубина_стека += 2;
прерви;
умолч:
положить_в_стек();
}
}
// Подготовка аргументов функции согласно x86-64 ABI
стат цел подготовить_аргументы_функции(Node *узел) {
цел стек_арг = 0, gp_счет = 0, fp_счет = 0;
если (узел->ret_buffer && узел->ty->size > 16)
gp_счет++;
для (Node *арг = узел->args; арг; арг = арг->next) {
Type *тип_арг = арг->ty;
выбор (тип_арг->kind) {
случай TY_STRUCT:
случай TY_UNION:
если (тип_арг->size > 16) {
арг->pass_by_stack = истина;
стек_арг += align_to(тип_арг->size, 8) / 8;
} иначе {
бул fp1 = первые_8_байт_дробные(тип_арг);
бул fp2 = вторые_8_байт_дробные(тип_арг);
если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) {
fp_счет = fp_счет + fp1 + fp2;
gp_счет = gp_счет + !fp1 + !fp2;
} иначе {
арг->pass_by_stack = истина;
стек_арг += align_to(тип_арг->size, 8) / 8;
}
}
прерви;
случай TY_FLOAT:
случай TY_DOUBLE:
если (fp_счет++ >= FP_MAX) {
арг->pass_by_stack = истина;
стек_арг++;
}
прерви;
случай TY_LDOUBLE:
арг->pass_by_stack = истина;
стек_арг += 2;
прерви;
умолч:
если (gp_счет++ >= GP_MAX) {
арг->pass_by_stack = истина;
стек_арг++;
}
}
}
если ((глубина_стека + стек_арг) % 2 == 1) {
вывести_строку(" sub $8, %%rsp");
глубина_стека++;
стек_арг++;
}
обработать_аргументы_функции(узел->args, true);
обработать_аргументы_функции(узел->args, false);
если (узел->ret_buffer && узел->ty->size > 16) {
вывести_строку(" lea %d(%%rbp), %%rax", узел->ret_buffer->offset);
положить_в_стек();
}
возврат стек_арг;
}
// Копирование возвращаемого значения структуры в буфер
стат пусто скопировать_буферозврата(Obj *перем) {
Type *тип_перем = перем->ty;
цел gp_счет = 0, fp_счет = 0;
если (первые_8_байт_дробные(тип_перем)) {
assert(тип_перем->size == 4 || 8 <= тип_перем->size);
если (тип_перем->size == 4)
вывести_строку(" movss %%xmm0, %d(%%rbp)", перем->offset);
иначе
вывести_строку(" movsd %%xmm0, %d(%%rbp)", перем->offset);
fp_счет++;
} иначе {
для (цел i = 0; i < MIN(8, тип_перем->size); i++) {
вывести_строку(" mov %%al, %d(%%rbp)", перем->offset + i);
вывести_строку(" shr $8, %%rax");
}
gp_счет++;
}
если (тип_перем->size > 8) {
если (вторые_8_байт_дробные(тип_перем)) {
assert(тип_перем->size == 12 || тип_перем->size == 16);
если (тип_перем->size == 12)
вывести_строку(" movss %%xmm%d, %d(%%rbp)", fp_счет, перем->offset + 8);
иначе
вывести_строку(" movsd %%xmm%d, %d(%%rbp)", fp_счет, перем->offset + 8);
} иначе {
символ *рег1 = (gp_счет == 0) ? "%al" : "%dl";
символ *рег2 = (gp_счет == 0) ? "%rax" : "%rdx";
для (цел i = 8; i < MIN(16, тип_перем->size); i++) {
вывести_строку(" mov %s, %d(%%rbp)", рег1, перем->offset + i);
вывести_строку(" shr $8, %s", рег2);
}
}
}
}
// Копирование структуры в регистры для возврата
стат пусто скопировать_структуру_в_регистры(пусто) {
Type *типозврата = текущаяункция->ty->return_ty;
цел gp_счет = 0, fp_счет = 0;
вывести_строку(" mov %%rax, %%rdi");
если (толькоробные_члены(типозврата, 0, 8, 0)) {
assert(типозврата->size == 4 || 8 <= типозврата->size);
если (типозврата->size == 4)
вывести_строку(" movss (%%rdi), %%xmm0");
иначе
вывести_строку(" movsd (%%rdi), %%xmm0");
fp_счет++;
} иначе {
вывести_строку(" mov $0, %%rax");
для (цел i = MIN(8, типозврата->size) - 1; i >= 0; i--) {
вывести_строку(" shl $8, %%rax");
вывести_строку(" mov %d(%%rdi), %%al", i);
}
gp_счет++;
}
если (типозврата->size > 8) {
если (толькоробные_члены(типозврата, 8, 16, 0)) {
assert(типозврата->size == 12 || типозврата->size == 16);
если (типозврата->size == 4)
вывести_строку(" movss 8(%%rdi), %%xmm%d", fp_счет);
иначе
вывести_строку(" movsd 8(%%rdi), %%xmm%d", fp_счет);
} иначе {
символ *рег1 = (gp_счет == 0) ? "%al" : "%dl";
символ *рег2 = (gp_счет == 0) ? "%rax" : "%rdx";
вывести_строку(" mov $0, %s", рег2);
для (цел i = MIN(16, типозврата->size) - 1; i >= 8; i--) {
вывести_строку(" shl $8, %s", рег2);
вывести_строку(" mov %d(%%rdi), %s", i, рег1);
}
}
}
}
// Копирование структуры в память для возврата
стат пусто скопировать_структуру_в_память(пусто) {
Type *типозврата = текущаяункция->ty->return_ty;
Obj *парам = текущаяункция->params;
вывести_строку(" mov %d(%%rbp), %%rdi", парам->offset);
для (цел i = 0; i < типозврата->size; i++) {
вывести_строку(" mov %d(%%rax), %%dl", i);
вывести_строку(" mov %%dl, %d(%%rdi)", i);
}
}
// Встроенная функция alloca
стат пусто сгенерировать_alloca(пусто) {
вывести_строку(" add $15, %%rdi");
вывести_строку(" and $0xfffffff0, %%edi");
вывести_строку(" mov %d(%%rbp), %%rcx", текущаяункция->alloca_bottom->offset);
вывести_строку(" sub %%rsp, %%rcx");
вывести_строку(" mov %%rsp, %%rax");
вывести_строку(" sub %%rdi, %%rsp");
вывести_строку(" mov %%rsp, %%rdx");
вывести_строку("1:");
вывести_строку(" cmp $0, %%rcx");
вывести_строку(" je 2f");
вывести_строку(" mov (%%rax), %%r8b");
вывести_строку(" mov %%r8b, (%%rdx)");
вывести_строку(" inc %%rdx");
вывести_строку(" inc %%rax");
вывести_строку(" dec %%rcx");
вывести_строку(" jmp 1b");
вывести_строку("2:");
вывести_строку(" mov %d(%%rbp), %%rax", текущаяункция->alloca_bottom->offset);
вывести_строку(" sub %%rdi, %%rax");
вывести_строку(" mov %%rax, %d(%%rbp)", текущаяункция->alloca_bottom->offset);
}
// Генерация кода для выражения
стат пусто сгенерировать_выражение(Node *узел) {
вывести_строку(" .loc %d %d", узел->tok->file->file_no, узел->tok->line_no);
выбор (узел->kind) {
случай ND_NULL_EXPR:
возврат;
случай ND_NUM: {
выбор (узел->ty->kind) {
случай TY_FLOAT: {
объед { плав f32; uint32_t u32; } u = { узел->fval };
вывести_строку(" mov $%u, %%eax # float %Lf", u.u32, узел->fval);
вывести_строку(" movq %%rax, %%xmm0");
возврат;
}
случай TY_DOUBLE: {
объед { двойн f64; uint64_t u64; } u = { узел->fval };
вывести_строку(" mov $%lu, %%rax # double %Lf", u.u64, узел->fval);
вывести_строку(" movq %%rax, %%xmm0");
возврат;
}
случай TY_LDOUBLE: {
объед { длин двойн f80; uint64_t u64[2]; } u;
memset(&u, 0, размер(u));
u.f80 = узел->fval;
вывести_строку(" mov $%lu, %%rax # long double %Lf", u.u64[0], узел->fval);
вывести_строку(" mov %%rax, -16(%%rsp)");
вывести_строку(" mov $%lu, %%rax", u.u64[1]);
вывести_строку(" mov %%rax, -8(%%rsp)");
вывести_строку(" fldt -16(%%rsp)");
возврат;
}
}
вывести_строку(" mov $%ld, %%rax", узел->val);
возврат;
}
случай ND_NEG:
сгенерировать_выражение(узел->lhs);
выбор (узел->ty->kind) {
случай TY_FLOAT:
вывести_строку(" mov $1, %%rax");
вывести_строку(" shl $31, %%rax");
вывести_строку(" movq %%rax, %%xmm1");
вывести_строку(" xorps %%xmm1, %%xmm0");
возврат;
случай TY_DOUBLE:
вывести_строку(" mov $1, %%rax");
вывести_строку(" shl $63, %%rax");
вывести_строку(" movq %%rax, %%xmm1");
вывести_строку(" xorpd %%xmm1, %%xmm0");
возврат;
случай TY_LDOUBLE:
вывести_строку(" fchs");
возврат;
}
вывести_строку(" neg %%rax");
возврат;
случай ND_VAR:
сгенерировать_адрес(узел);
загрузить_значение(узел->ty);
возврат;
случай ND_MEMBER: {
сгенерировать_адрес(узел);
загрузить_значение(узел->ty);
Member *член = узел->member;
если (член->is_bitfield) {
вывести_строку(" shl $%d, %%rax", 64 - член->bit_width - член->bit_offset);
если (член->ty->is_unsigned)
вывести_строку(" shr $%d, %%rax", 64 - член->bit_width);
иначе
вывести_строку(" sar $%d, %%rax", 64 - член->bit_width);
}
возврат;
}
случай ND_DEREF:
сгенерировать_выражение(узел->lhs);
загрузить_значение(узел->ty);
возврат;
случай ND_ADDR:
сгенерировать_адрес(узел->lhs);
возврат;
случай ND_ASSIGN:
сгенерировать_адрес(узел->lhs);
положить_в_стек();
сгенерировать_выражение(узел->rhs);
если (узел->lhs->kind == ND_MEMBER && узел->lhs->member->is_bitfield) {
вывести_строку(" mov %%rax, %%r8");
Member *член = узел->lhs->member;
вывести_строку(" mov %%rax, %%rdi");
вывести_строку(" and $%ld, %%rdi", (1L << член->bit_width) - 1);
вывести_строку(" shl $%d, %%rdi", член->bit_offset);
вывести_строку(" mov (%%rsp), %%rax");
загрузить_значение(член->ty);
длин маска = ((1L << член->bit_width) - 1) << член->bit_offset;
вывести_строку(" mov $%ld, %%r9", ~маска);
вывести_строку(" and %%r9, %%rax");
вывести_строку(" or %%rdi, %%rax");
сохранить_значение(узел->ty);
вывести_строку(" mov %%r8, %%rax");
возврат;
}
сохранить_значение(узел->ty);
возврат;
случай ND_STMT_EXPR:
для (Node *n = узел->body; n; n = n->next)
сгенерировать_оператор(n);
возврат;
случай ND_COMMA:
сгенерировать_выражение(узел->lhs);
сгенерировать_выражение(узел->rhs);
возврат;
случай ND_CAST:
сгенерировать_выражение(узел->lhs);
сгенерировать_приведение_типа(узел->lhs->ty, узел->ty);
возврат;
случай ND_MEMZERO:
вывести_строку(" mov $%d, %%rcx", узел->var->ty->size);
вывести_строку(" lea %d(%%rbp), %%rdi", узел->var->offset);
вывести_строку(" mov $0, %%al");
вывести_строку(" rep stosb");
возврат;
случай ND_COND: {
цел метка = сгенерироватьомеретки();
сгенерировать_выражение(узел->cond);
сравнить_сулем(узел->cond->ty);
вывести_строку(" je .L.else.%d", метка);
сгенерировать_выражение(узел->then);
вывести_строку(" jmp .L.end.%d", метка);
вывести_строку(".L.else.%d:", метка);
сгенерировать_выражение(узел->els);
вывести_строку(".L.end.%d:", метка);
возврат;
}
случай ND_NOT:
сгенерировать_выражение(узел->lhs);
сравнить_сулем(узел->lhs->ty);
вывести_строку(" sete %%al");
вывести_строку(" movzx %%al, %%rax");
возврат;
случай ND_BITNOT:
сгенерировать_выражение(узел->lhs);
вывести_строку(" not %%rax");
возврат;
случай ND_LOGAND: {
цел метка = сгенерироватьомеретки();
сгенерировать_выражение(узел->lhs);
сравнить_сулем(узел->lhs->ty);
вывести_строку(" je .L.false.%d", метка);
сгенерировать_выражение(узел->rhs);
сравнить_сулем(узел->rhs->ty);
вывести_строку(" je .L.false.%d", метка);
вывести_строку(" mov $1, %%rax");
вывести_строку(" jmp .L.end.%d", метка);
вывести_строку(".L.false.%d:", метка);
вывести_строку(" mov $0, %%rax");
вывести_строку(".L.end.%d:", метка);
возврат;
}
случай ND_LOGOR: {
цел метка = сгенерироватьомеретки();
сгенерировать_выражение(узел->lhs);
сравнить_сулем(узел->lhs->ty);
вывести_строку(" jne .L.true.%d", метка);
сгенерировать_выражение(узел->rhs);
сравнить_сулем(узел->rhs->ty);
вывести_строку(" jne .L.true.%d", метка);
вывести_строку(" mov $0, %%rax");
вывести_строку(" jmp .L.end.%d", метка);
вывести_строку(".L.true.%d:", метка);
вывести_строку(" mov $1, %%rax");
вывести_строку(".L.end.%d:", метка);
возврат;
}
случай ND_FUNCALL: {
если (узел->lhs->kind == ND_VAR && !strcmp(узел->lhs->var->name, "alloca")) {
сгенерировать_выражение(узел->args);
вывести_строку(" mov %%rax, %%rdi");
сгенерировать_alloca();
возврат;
}
цел стек_арг = подготовить_аргументы_функции(узел);
сгенерировать_выражение(узел->lhs);
цел gp_счет = 0, fp_счет = 0;
если (узел->ret_buffer && узел->ty->size > 16)
извлечь_из_стека(арг_рег64[gp_счет++]);
для (Node *арг = узел->args; арг; арг = арг->next) {
Type *тип_арг = арг->ty;
выбор (тип_арг->kind) {
случай TY_STRUCT:
случай TY_UNION:
если (тип_арг->size > 16)
продолжи;
бул fp1 = первые_8_байт_дробные(тип_арг);
бул fp2 = вторые_8_байт_дробные(тип_арг);
если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) {
если (fp1)
извлечьробное_из_стека(fp_счет++);
иначе
извлечь_из_стека(арг_рег64[gp_счет++]);
если (тип_арг->size > 8) {
если (fp2)
извлечьробное_из_стека(fp_счет++);
иначе
извлечь_из_стека(арг_рег64[gp_счет++]);
}
}
прерви;
случай TY_FLOAT:
случай TY_DOUBLE:
если (fp_счет < FP_MAX)
извлечьробное_из_стека(fp_счет++);
прерви;
случай TY_LDOUBLE:
прерви;
умолч:
если (gp_счет < GP_MAX)
извлечь_из_стека(арг_рег64[gp_счет++]);
}
}
вывести_строку(" mov %%rax, %%r10");
вывести_строку(" mov $%d, %%rax", fp_счет);
вывести_строку(" call *%%r10");
вывести_строку(" add $%d, %%rsp", стек_арг * 8);
глубина_стека -= стек_арг;
выбор (узел->ty->kind) {
случай TY_BOOL:
вывести_строку(" movzx %%al, %%eax");
возврат;
случай TY_CHAR:
если (узел->ty->is_unsigned)
вывести_строку(" movzbl %%al, %%eax");
иначе
вывести_строку(" movsbl %%al, %%eax");
возврат;
случай TY_SHORT:
если (узел->ty->is_unsigned)
вывести_строку(" movzwl %%ax, %%eax");
иначе
вывести_строку(" movswl %%ax, %%eax");
возврат;
}
если (узел->ret_buffer && узел->ty->size <= 16) {
скопировать_буферозврата(узел->ret_buffer);
вывести_строку(" lea %d(%%rbp), %%rax", узел->ret_buffer->offset);
}
возврат;
}
случай ND_LABEL_VAL:
вывести_строку(" lea %s(%%rip), %%rax", узел->unique_label);
возврат;
случай ND_CAS: {
сгенерировать_выражение(узел->cas_addr);
положить_в_стек();
сгенерировать_выражение(узел->cas_new);
положить_в_стек();
сгенерировать_выражение(узел->cas_old);
вывести_строку(" mov %%rax, %%r8");
загрузить_значение(узел->cas_old->ty->base);
извлечь_из_стека("%rdx");
извлечь_из_стека("%rdi");
цел разм = узел->cas_addr->ty->base->size;
вывести_строку(" lock cmpxchg %s, (%%rdi)", получить_рег_dx(разм));
вывести_строку(" sete %%cl");
вывести_строку(" je 1f");
вывести_строку(" mov %s, (%%r8)", получить_рег_ax(разм));
вывести_строку("1:");
вывести_строку(" movzbl %%cl, %%eax");
возврат;
}
случай ND_EXCH: {
сгенерировать_выражение(узел->lhs);
положить_в_стек();
сгенерировать_выражение(узел->rhs);
извлечь_из_стека("%rdi");
цел разм = узел->lhs->ty->base->size;
вывести_строку(" xchg %s, (%%rdi)", получить_рег_ax(разм));
возврат;
}
}
// Арифметические операции с плавающей точкой
выбор (узел->lhs->ty->kind) {
случай TY_FLOAT:
случай TY_DOUBLE: {
сгенерировать_выражение(узел->rhs);
положитьробное_в_стек();
сгенерировать_выражение(узел->lhs);
извлечьробное_из_стека(1);
символ *суффикс = (узел->lhs->ty->kind == TY_FLOAT) ? "ss" : "sd";
выбор (узел->kind) {
случай ND_ADD:
вывести_строку(" add%s %%xmm1, %%xmm0", суффикс);
возврат;
случай ND_SUB:
вывести_строку(" sub%s %%xmm1, %%xmm0", суффикс);
возврат;
случай ND_MUL:
вывести_строку(" mul%s %%xmm1, %%xmm0", суффикс);
возврат;
случай ND_DIV:
вывести_строку(" div%s %%xmm1, %%xmm0", суффикс);
возврат;
случай ND_EQ:
случай ND_NE:
случай ND_LT:
случай ND_LE:
вывести_строку(" ucomi%s %%xmm0, %%xmm1", суффикс);
если (узел->kind == ND_EQ) {
вывести_строку(" sete %%al");
вывести_строку(" setnp %%dl");
вывести_строку(" and %%dl, %%al");
} иначе если (узел->kind == ND_NE) {
вывести_строку(" setne %%al");
вывести_строку(" setp %%dl");
вывести_строку(" or %%dl, %%al");
} иначе если (узел->kind == ND_LT) {
вывести_строку(" seta %%al");
} иначе {
вывести_строку(" setae %%al");
}
вывести_строку(" and $1, %%al");
вывести_строку(" movzb %%al, %%rax");
возврат;
}
error_tok(узел->tok, "invalid expression");
}
случай TY_LDOUBLE: {
сгенерировать_выражение(узел->lhs);
сгенерировать_выражение(узел->rhs);
выбор (узел->kind) {
случай ND_ADD:
вывести_строку(" faddp");
возврат;
случай ND_SUB:
вывести_строку(" fsubrp");
возврат;
случай ND_MUL:
вывести_строку(" fmulp");
возврат;
случай ND_DIV:
вывести_строку(" fdivrp");
возврат;
случай ND_EQ:
случай ND_NE:
случай ND_LT:
случай ND_LE:
вывести_строку(" fcomip");
вывести_строку(" fstp %%st(0)");
если (узел->kind == ND_EQ)
вывести_строку(" sete %%al");
иначе если (узел->kind == ND_NE)
вывести_строку(" setne %%al");
иначе если (узел->kind == ND_LT)
вывести_строку(" seta %%al");
иначе
вывести_строку(" setae %%al");
вывести_строку(" movzb %%al, %%rax");
возврат;
}
error_tok(узел->tok, "invalid expression");
}
}
// Целочисленные арифметические операции
сгенерировать_выражение(узел->rhs);
положить_в_стек();
сгенерировать_выражение(узел->lhs);
извлечь_из_стека("%rdi");
символ *ax_рег, *di_рег, *dx_рег;
если (узел->lhs->ty->kind == TY_LONG || узел->lhs->ty->base) {
ax_рег = "%rax";
di_рег = "%rdi";
dx_рег = "%rdx";
} иначе {
ax_рег = "%eax";
di_рег = "%edi";
dx_рег = "%edx";
}
выбор (узел->kind) {
случай ND_ADD:
вывести_строку(" add %s, %s", di_рег, ax_рег);
возврат;
случай ND_SUB:
вывести_строку(" sub %s, %s", di_рег, ax_рег);
возврат;
случай ND_MUL:
вывести_строку(" imul %s, %s", di_рег, ax_рег);
возврат;
случай ND_DIV:
случай ND_MOD:
если (узел->ty->is_unsigned) {
вывести_строку(" mov $0, %s", dx_рег);
вывести_строку(" div %s", di_рег);
} иначе {
если (узел->lhs->ty->size == 8)
вывести_строку(" cqo");
иначе
вывести_строку(" cdq");
вывести_строку(" idiv %s", di_рег);
}
если (узел->kind == ND_MOD)
вывести_строку(" mov %%rdx, %%rax");
возврат;
случай ND_BITAND:
вывести_строку(" and %s, %s", di_рег, ax_рег);
возврат;
случай ND_BITOR:
вывести_строку(" or %s, %s", di_рег, ax_рег);
возврат;
случай ND_BITXOR:
вывести_строку(" xor %s, %s", di_рег, ax_рег);
возврат;
случай ND_EQ:
случай ND_NE:
случай ND_LT:
случай ND_LE:
вывести_строку(" cmp %s, %s", di_рег, ax_рег);
если (узел->kind == ND_EQ) {
вывести_строку(" sete %%al");
} иначе если (узел->kind == ND_NE) {
вывести_строку(" setne %%al");
} иначе если (узел->kind == ND_LT) {
если (узел->lhs->ty->is_unsigned)
вывести_строку(" setb %%al");
иначе
вывести_строку(" setl %%al");
} иначе если (узел->kind == ND_LE) {
если (узел->lhs->ty->is_unsigned)
вывести_строку(" setbe %%al");
иначе
вывести_строку(" setle %%al");
}
вывести_строку(" movzb %%al, %%rax");
возврат;
случай ND_SHL:
вывести_строку(" mov %%rdi, %%rcx");
вывести_строку(" shl %%cl, %s", ax_рег);
возврат;
случай ND_SHR:
вывести_строку(" mov %%rdi, %%rcx");
если (узел->lhs->ty->is_unsigned)
вывести_строку(" shr %%cl, %s", ax_рег);
иначе
вывести_строку(" sar %%cl, %s", ax_рег);
возврат;
}
error_tok(узел->tok, "invalid expression");
}
// Генерация кода для оператора
стат пусто сгенерировать_оператор(Node *узел) {
вывести_строку(" .loc %d %d", узел->tok->file->file_no, узел->tok->line_no);
выбор (узел->kind) {
случай ND_IF: {
цел метка = сгенерироватьомеретки();
сгенерировать_выражение(узел->cond);
сравнить_сулем(узел->cond->ty);
вывести_строку(" je .L.else.%d", метка);
сгенерировать_оператор(узел->then);
вывести_строку(" jmp .L.end.%d", метка);
вывести_строку(".L.else.%d:", метка);
если (узел->els)
сгенерировать_оператор(узел->els);
вывести_строку(".L.end.%d:", метка);
возврат;
}
случай ND_FOR: {
цел метка = сгенерироватьомеретки();
если (узел->init)
сгенерировать_оператор(узел->init);
вывести_строку(".L.begin.%d:", метка);
если (узел->cond) {
сгенерировать_выражение(узел->cond);
сравнить_сулем(узел->cond->ty);
вывести_строку(" je %s", узел->brk_label);
}
сгенерировать_оператор(узел->then);
вывести_строку("%s:", узел->cont_label);
если (узел->inc)
сгенерировать_выражение(узел->inc);
вывести_строку(" jmp .L.begin.%d", метка);
вывести_строку("%s:", узел->brk_label);
возврат;
}
случай ND_DO: {
цел метка = сгенерироватьомеретки();
вывести_строку(".L.begin.%d:", метка);
сгенерировать_оператор(узел->then);
вывести_строку("%s:", узел->cont_label);
сгенерировать_выражение(узел->cond);
сравнить_сулем(узел->cond->ty);
вывести_строку(" jne .L.begin.%d", метка);
вывести_строку("%s:", узел->brk_label);
возврат;
}
случай ND_SWITCH:
сгенерировать_выражение(узел->cond);
для (Node *n = узел->case_next; n; n = n->case_next) {
символ *ax_рег = (узел->cond->ty->size == 8) ? "%rax" : "%eax";
символ *di_рег = (узел->cond->ty->size == 8) ? "%rdi" : "%edi";
если (n->begin == n->end) {
вывести_строку(" cmp $%ld, %s", n->begin, ax_рег);
вывести_строку(" je %s", n->label);
продолжи;
}
вывести_строку(" mov %s, %s", ax_рег, di_рег);
вывести_строку(" sub $%ld, %s", n->begin, di_рег);
вывести_строку(" cmp $%ld, %s", n->end - n->begin, di_рег);
вывести_строку(" jbe %s", n->label);
}
если (узел->default_case)
вывести_строку(" jmp %s", узел->default_case->label);
вывести_строку(" jmp %s", узел->brk_label);
сгенерировать_оператор(узел->then);
вывести_строку("%s:", узел->brk_label);
возврат;
случай ND_CASE:
вывести_строку("%s:", узел->label);
сгенерировать_оператор(узел->lhs);
возврат;
случай ND_BLOCK:
для (Node *n = узел->body; n; n = n->next)
сгенерировать_оператор(n);
возврат;
случай ND_GOTO:
вывести_строку(" jmp %s", узел->unique_label);
возврат;
случай ND_GOTO_EXPR:
сгенерировать_выражение(узел->lhs);
вывести_строку(" jmp *%%rax");
возврат;
случай ND_LABEL:
вывести_строку("%s:", узел->unique_label);
сгенерировать_оператор(узел->lhs);
возврат;
случай ND_RETURN:
если (узел->lhs) {
сгенерировать_выражение(узел->lhs);
Type *типозврата = узел->lhs->ty;
выбор (типозврата->kind) {
случай TY_STRUCT:
случай TY_UNION:
если (типозврата->size <= 16)
скопировать_структуру_в_регистры();
иначе
скопировать_структуру_в_память();
прерви;
}
}
вывести_строку(" jmp .L.return.%s", текущаяункция->name);
возврат;
случай ND_EXPR_STMT:
сгенерировать_выражение(узел->lhs);
возврат;
случай ND_ASM:
вывести_строку(" %s", узел->asm_str);
возврат;
}
error_tok(узел->tok, "invalid statement");
}
// Назначение смещений локальным переменным
стат пусто назначить_смещения_локальным(Obj *программа) {
для (Obj *функция = программа; функция; функция = функция->next) {
если (!функция->is_function)
продолжи;
цел верх = 16;
цел низ = 0;
цел gp_счет = 0, fp_счет = 0;
для (Obj *перем = функция->params; перем; перем = перем->next) {
Type *тип_перем = перем->ty;
выбор (тип_перем->kind) {
случай TY_STRUCT:
случай TY_UNION:
если (тип_перем->size <= 16) {
бул fp1 = толькоробные_члены(тип_перем, 0, 8, 0);
бул fp2 = толькоробные_члены(тип_перем, 8, 16, 8);
если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) {
fp_счет = fp_счет + fp1 + fp2;
gp_счет = gp_счет + !fp1 + !fp2;
продолжи;
}
}
прерви;
случай TY_FLOAT:
случай TY_DOUBLE:
если (fp_счет++ < FP_MAX)
продолжи;
прерви;
случай TY_LDOUBLE:
прерви;
умолч:
если (gp_счет++ < GP_MAX)
продолжи;
}
верх = align_to(верх, 8);
перем->offset = верх;
верх += перем->ty->size;
}
для (Obj *перем = функция->locals; перем; перем = перем->next) {
если (перем->offset)
продолжи;
цел выравнивание = (перем->ty->kind == TY_ARRAY && перем->ty->size >= 16)
? MAX(16, перем->align) : перем->align;
низ += перем->ty->size;
низ = align_to(низ, выравнивание);
перем->offset = -низ;
}
функция->stack_size = align_to(низ, 16);
}
}
// Генерация секции данных
стат пусто создать_секцию_данных(Obj *программа) {
для (Obj *перем = программа; перем; перем = перем->next) {
если (перем->is_function || !перем->is_definition)
продолжи;
если (перем->is_static)
вывести_строку(" .local %s", перем->name);
иначе
вывести_строку(" .globl %s", перем->name);
цел выравнивание = (перем->ty->kind == TY_ARRAY && перем->ty->size >= 16)
? MAX(16, перем->align) : перем->align;
если (opt_fcommon && перем->is_tentative) {
вывести_строку(" .comm %s, %d, %d", перем->name, перем->ty->size, выравнивание);
продолжи;
}
если (перем->init_data) {
если (перем->is_tls)
вывести_строку(" .section .tdata,\"awT\",@progbits");
иначе
вывести_строку(" .data");
вывести_строку(" .type %s, @object", перем->name);
вывести_строку(" .size %s, %d", перем->name, перем->ty->size);
вывести_строку(" .align %d", выравнивание);
вывести_строку("%s:", перем->name);
Relocation *рел = перем->rel;
цел позиция = 0;
пока (позиция < перем->ty->size) {
если (рел && рел->offset == позиция) {
вывести_строку(" .quad %s%+ld", *рел->label, рел->addend);
рел = рел->next;
позиция += 8;
} иначе {
вывести_строку(" .byte %d", перем->init_data[позиция++]);
}
}
продолжи;
}
если (перем->is_tls)
вывести_строку(" .section .tbss,\"awT\",@nobits");
иначе
вывести_строку(" .bss");
вывести_строку(" .align %d", выравнивание);
вывести_строку("%s:", перем->name);
вывести_строку(" .zero %d", перем->ty->size);
}
}
// Сохранение значения из XMM регистра в стек
стат пусто сохранитьробный_регистр(цел номер_рег, цел смещение, цел разм) {
выбор (разм) {
случай 4:
вывести_строку(" movss %%xmm%d, %d(%%rbp)", номер_рег, смещение);
возврат;
случай 8:
вывести_строку(" movsd %%xmm%d, %d(%%rbp)", номер_рег, смещение);
возврат;
}
unreachable();
}
// Сохранение значения из GP регистра в стек
стат пусто сохранить_gp_регистр(цел номер_рег, цел смещение, цел разм) {
выбор (разм) {
случай 1:
вывести_строку(" mov %s, %d(%%rbp)", арг_рег8[номер_рег], смещение);
возврат;
случай 2:
вывести_строку(" mov %s, %d(%%rbp)", арг_рег16[номер_рег], смещение);
возврат;
случай 4:
вывести_строку(" mov %s, %d(%%rbp)", арг_рег32[номер_рег], смещение);
возврат;
случай 8:
вывести_строку(" mov %s, %d(%%rbp)", арг_рег64[номер_рег], смещение);
возврат;
умолч:
для (цел i = 0; i < разм; i++) {
вывести_строку(" mov %s, %d(%%rbp)", арг_рег8[номер_рег], смещение + i);
вывести_строку(" shr $8, %s", арг_рег64[номер_рег]);
}
возврат;
}
}
// Генерация секции кода
стат пусто создать_секцию_кода(Obj *программа) {
для (Obj *функция = программа; функция; функция = функция->next) {
если (!функция->is_function || !функция->is_definition)
продолжи;
если (!функция->is_live)
продолжи;
если (функция->is_static)
вывести_строку(" .local %s", функция->name);
иначе
вывести_строку(" .globl %s", функция->name);
если (strcmp(функция->name, "главн") == 0)
вывести_строку(" .globl main");
вывести_строку(" .text");
вывести_строку(" .type %s, @function", функция->name);
вывести_строку("%s:", функция->name);
если (strcmp(функция->name, "главн") == 0)
вывести_строку("main:");
текущаяункция = функция;
вывести_строку(" push %%rbp");
вывести_строку(" mov %%rsp, %%rbp");
вывести_строку(" sub $%d, %%rsp", функция->stack_size);
вывести_строку(" mov %%rsp, %d(%%rbp)", функция->alloca_bottom->offset);
если (функция->va_area) {
цел gp_счет = 0, fp_счет = 0;
для (Obj *перем = функция->params; перем; перем = перем->next) {
если (is_flonum(перем->ty))
fp_счет++;
иначе
gp_счет++;
}
цел смещение = функция->va_area->offset;
вывести_строку(" movl $%d, %d(%%rbp)", gp_счет * 8, смещение);
вывести_строку(" movl $%d, %d(%%rbp)", fp_счет * 8 + 48, смещение + 4);
вывести_строку(" movq %%rbp, %d(%%rbp)", смещение + 8);
вывести_строку(" addq $16, %d(%%rbp)", смещение + 8);
вывести_строку(" movq %%rbp, %d(%%rbp)", смещение + 16);
вывести_строку(" addq $%d, %d(%%rbp)", смещение + 24, смещение + 16);
вывести_строку(" movq %%rdi, %d(%%rbp)", смещение + 24);
вывести_строку(" movq %%rsi, %d(%%rbp)", смещение + 32);
вывести_строку(" movq %%rdx, %d(%%rbp)", смещение + 40);
вывести_строку(" movq %%rcx, %d(%%rbp)", смещение + 48);
вывести_строку(" movq %%r8, %d(%%rbp)", смещение + 56);
вывести_строку(" movq %%r9, %d(%%rbp)", смещение + 64);
вывести_строку(" movsd %%xmm0, %d(%%rbp)", смещение + 72);
вывести_строку(" movsd %%xmm1, %d(%%rbp)", смещение + 80);
вывести_строку(" movsd %%xmm2, %d(%%rbp)", смещение + 88);
вывести_строку(" movsd %%xmm3, %d(%%rbp)", смещение + 96);
вывести_строку(" movsd %%xmm4, %d(%%rbp)", смещение + 104);
вывести_строку(" movsd %%xmm5, %d(%%rbp)", смещение + 112);
вывести_строку(" movsd %%xmm6, %d(%%rbp)", смещение + 120);
вывести_строку(" movsd %%xmm7, %d(%%rbp)", смещение + 128);
}
цел gp_счет = 0, fp_счет = 0;
для (Obj *перем = функция->params; перем; перем = перем->next) {
если (перем->offset > 0)
продолжи;
Type *тип_перем = перем->ty;
выбор (тип_перем->kind) {
случай TY_STRUCT:
случай TY_UNION:
assert(тип_перем->size <= 16);
если (толькоробные_члены(тип_перем, 0, 8, 0))
сохранитьробный_регистр(fp_счет++, перем->offset, MIN(8, тип_перем->size));
иначе
сохранить_gp_регистр(gp_счет++, перем->offset, MIN(8, тип_перем->size));
если (тип_перем->size > 8) {
если (толькоробные_члены(тип_перем, 8, 16, 0))
сохранитьробный_регистр(fp_счет++, перем->offset + 8, тип_перем->size - 8);
иначе
сохранить_gp_регистр(gp_счет++, перем->offset + 8, тип_перем->size - 8);
}
прерви;
случай TY_FLOAT:
случай TY_DOUBLE:
сохранитьробный_регистр(fp_счет++, перем->offset, тип_перем->size);
прерви;
умолч:
сохранить_gp_регистр(gp_счет++, перем->offset, тип_перем->size);
}
}
сгенерировать_оператор(функция->body);
assert(глубина_стека == 0);
если (strcmp(функция->name, "main") == 0 || strcmp(функция->name, "главн") == 0)
вывести_строку(" mov $0, %%rax");
вывести_строку(".L.return.%s:", функция->name);
вывести_строку(" mov %%rbp, %%rsp");
вывести_строку(" pop %%rbp");
вывести_строку(" ret");
}
}
// Главная функция генерации кода
пусто codegen(Obj *программа, FILE *выход) {
выходнойайл = выход;
File **файлы = get_input_files();
для (цел i = 0; файлы[i]; i++)
вывести_строку(" .file %d \"%s\"", файлы[i]->file_no, файлы[i]->name);
назначить_смещения_локальным(программа);
создать_секцию_данных(программа);
создать_секцию_кода(программа);
}