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