Неопределенное поведение после нескольких вызовов функции печати - PullRequest
1 голос
/ 06 апреля 2019

Я пишу базовый компилятор, и сгенерированный код не работает должным образом.
Я использую наивный алгоритм раскраски графиков для распределения переменных в регистрах на основе их жизнеспособности.
Проблема в том, что сгенерированный ассемблерный код выглядит совершенно нормально, но в какой-то момент он вызывает неопределенное поведение.

Если вместо использования регистров для хранения переменных я просто использую стек, все работает нормально.
Я также обнаружил, что не могу использовать регистр %edx вокруг инструкции imull, и мне стало интересно, происходит ли что-то подобное прямо сейчас с %ebx и %ecx.
Я компилирую код, используя gcc -m32 "test.s" runtime.c -o test, где runtime.c - вспомогательный файл C, содержащий функции печати и ввода.
Я также пытался удалить части программы (каждый отпечаток, кроме последнего), а затем будет работать последний отпечаток.
Если я вызову одну функцию печати до последнего вызова, она не будет работать.

Файл runtime.c:

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

int input() {
    int num;
    char term;
    scanf("%d%c", &num, &term);
    return num;
}

void print_int_nl(int i) {
    printf("%d\n", i);
}

Исходный файл:

a = 10
b = input()
c = - 10
d = -input()
print a
print b
print c
print d

Сгенерированный код сборки: https://pastebin.com/ChSRbWgt

После компиляции файла .s и запуска его с помощью консоли (./test) он запрашивает 2 ввода (как и предполагалось).
Я даю это 1 и 2.
Тогда вывод:

10
1
-10
1415880

вместо

10
1
-10
-2

Ответы [ 2 ]

2 голосов
/ 07 апреля 2019

Вы должны соблюдать соглашение о вызовах (см., Например, Соглашения о вызовах для различных компиляторов C ++ и операционных систем от Agner Fog ).

А именно, есть сохранение звонящего и сохранение звонящегорегистры в соглашении для вашего компилятора C.

Ваш сгенерированный код должен сохранить регистры сохранения вызываемого абонента, чтобы иметь возможность вернуться к своему вызывающему модулю C.

Аналогично, printf () будетсохранить регистры сохранения вызываемого абонента, но он может уничтожить регистры сохранения вызывающего абонента, а это означает, что если ваш сгенерированный код вызывает функцию printf (), ему необходимо будет сохранить регистры сохранения вызывающего абонента при вызовах printf () или любой другой функции C.

1 голос
/ 06 апреля 2019

Вам нужно очистить входной буфер после вашего первого ввода, буфер все еще содержит символ новой строки, я бы порекомендовал это:

int input() {
  int num;
  char term;
  scanf("%d %c", &num, &term);

  return num;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...