rusi/codegen.c

1568 lines
63 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#включить "руси.з"
// Максимальное количество аргументов в регистрах общего назначения и 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);
назначить_смещения_локальным(программа);
создать_секцию_данных(программа);
создать_секцию_кода(программа);
}