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