Запрет компилятору оптимизировать код без использования volatile - PullRequest
1 голос
/ 01 марта 2020

Я пытаюсь создать ядро ​​с нуля (просто пробую что-то новое)

Теперь все готово, я тестирую вывод и заметил что-то очень странное

Я строю свой файлы с:

gcc ./kernel/kernel.c -ffreestanding -O0 -m32 -c -o./bin/kernel.o -fno-pie

и связать их вместе с:

ld -nostdlib -nodefaultlibs -Tlink.ld ./bin/kernel_ep.elf.bin ./bin/kernel.o -o ./bin/kernel.bin

Итак, насколько я понимаю, я уже говорю компилятору НЕ оптимизировать мой код.

Теперь C part

#define BYTE unsigned char
#define VIDMEM ((BYTE*)0xb8000)

void init();
void main() {
    init();
    while(1);
}

void print(char *msg)
{
    volatile BYTE *screen = VIDMEM;
    for(const char *msgPtr = msg; *msgPtr; ++msgPtr)
    {
        *(screen++) = *msgPtr;
        screen++;
    }
}

void init() {
    //volatile char test[] = "Test";
    //print(test);
    print("Test");    
}

Если я выполню это, ничего не произойдет, я проверил все это в ghidra - массив char "Test" находится в памяти, но у меня нет ссылки на init () -> Так что print никогда не вызывается.

Если я сейчас использую закомментированный текст (и комментарий print("Test")), все работает нормально, текст печатается так, как я хочу.

Но реальный вопрос: есть ли какая-то «хитрость», чтобы сказать компилятору НЕ оптимизировать этот код, кроме использования volatile? потому что я не думаю, что объявление всего, что может быть напечатано как изменчивое, - это то, как я должен это делать.

Насколько я понимаю, главная проблема в том, что функция print в основном ничего не делает, потому что кажется, компилятор не знает, что 0xb8000 - это своего рода ... особый адрес.

Ответы [ 3 ]

3 голосов
/ 01 марта 2020
  1. Этот стиль определения типов в Microsoft просто ужасен. Используйте точные размеры, такие как uint8_t или int32_t. Определение BYTE - очень плохая привычка. Здесь проблем не будет, но проблема начинается с более широких типов и разных размеров в разных системах.

  2. Компилятор встроит обе функции в этом тривиальном примере. Таким образом, вы не увидите вызовы функций init или print. Если вы хотите, чтобы оно было ненулевым, используйте __attribute__((noinline)).

  3. , вам не нужно использовать volatile, так как код не будет оптимизирован.

#include <stdint.h>

#define VIDMEM ((uint8_t*)0xb8000)


void __attribute__((noinline)) print(const char *msg)
{
    uint8_t *screen = VIDMEM;
    for(const char *msgPtr = msg; *msgPtr; ++msgPtr)
    {
        *screen++ = *msgPtr; 
    }
}


void __attribute__((noinline)) init() {
    print("Test");    
}


void main() 
{
    init();
    while(1);
}

https://godbolt.org/z/iqNadv

Здесь у вас есть встроенная версия. Функция stati c предотвращает получение компилятором другой копии для внешней ссылки.

https://godbolt.org/z/giKAnC

0 голосов
/ 01 марта 2020

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

0 голосов
/ 01 марта 2020

В то время как компиляторы рассматривали преобразования целочисленных значений в указатели как указывающие точки, в которых компиляторы не должны предполагать ничего о результирующем указателе (по сути, подразумевая изменчивую семантику в случае, когда результат каждого акта преобразования использовался только один раз), ни clang, ни g cc предоставляет любую опцию для поддержки такой семантики, кроме -O0.

...