Adresy labelów w bajtkodzie

0

Otóż parę tygodni temu wprowadziłem do mojego języka wskaźniki na funkcje (póki co jedynie na GitHubie, ale najnowsza wersja kompilatora już blisko :>).
Działa to na takiej zasadzie:

@("stdlib\\stdio.ss")

use std; // `std` -> standardowa przestrzeń nazw funkcji będących w "standardzie" języka. `use` działa jak C++owe `using namespace`.

function<void> zrob_cos()
{
 println("Hello World!");
}

function<int> main()
{
 var<function<void>()> func = zrob_cos; // sama nazwa funkcji oznacza jej adres - podobnie jak w Object Pascalu; w C++ ten kod byłby czymś w rodzaju `func = &zrob_cos;` - tak dla uzupełnienia

 func(); // wywołanie funkcji, na którą wskazuje wskaźnik

 return 0;
}

W bajtkodzie wygląda to tak:

call(:__function_self_main__int_)
stop()

__function_self_zrob_cos__void_:
  add(stp,1) // tak naprawdę zbędne, ale jest to dodawane przez kompilator "tak-w-razie-gdyby". Chodzi o to, że podczas wywoływania opcodu `call()`, na stos wrzucony zostaje instruction-pointer. Wcześniej był osobno callstack oraz osobno stos programu, lecz teraz jest to połączone, stąd też to `add` w tym miejscu, oraz `sub` na końcu. Może potem pomyślę jakby się tego pozbyć or something :P
__function_self_zrob_cos__void__body:
  push("Hello World!")
  call(:__function_std_println)  
  sub(stp,1) // konwencja wywoływania na zasadzie `cdecl` - funkcja, która wywołuje jakąś inną, czyści po niej stos. `stp` to pozycja stosu.
__function_self_zrob_cos__void__end:
  sub(stp,1)
  ret()

__function_self_main__int_:
  add(stp,1)
  push(er3) // pierwsza zmienna wczytana jest do tego rejestru, więc musimy go wrzucić na stos
__function_self_main__int__body:
  mov(er3,@__function_self_zrob_cos__void_) // zaraz wytłumaczę o co z tym chodzi :P
  acall(@__function_self_zrob_cos__void_)
  { krótkie wytłumaczenie:
   Ten kod na początku wyglądał tak:
     mov(er3, @__function...)
     mov(er1, er3)
     acall(er1)
   Lecz wyniku propagacji kopiowania, zostało to skrócone do:
     mov(er3, @__function...)
     acall(@__function...)

   Optymalizator nie może usunąć tego pierwszego przypisania (do rejestru `er3`), ponieważ jest to przypisanie do rejestru trzymającego zmienną (nawet mimo tego, że nie wpłynęłoby to w żaden sposób na kod :P).
   Potem dodam propagację kopiowania w optymalizatorze wyrażeń (póki co posiada ją jedynie optymalizator bajtkodu), więc to już zostanie bezproblemowo rozwinięte do samego `acall`.
   ---------------------------
   Also: istnieją 3 wywołania typu `call`:
   call(adres) - skacze pod `instruction_pointer+adres` (tj.po prostu `adres` opcodów dalej; jak zwykłe `call` na x86).
   acall(adres) - skacze bezpośrednio pod `adres`; pod x86 bodajże to jest `call 0x8:adres` czy coś w tym rodzaju :P
   icall(string) - wywołanie komendy maszyny wirtualnej, np.`output.print`
  }
  mov(ei1,0) // to jest to nasze `return 0;`
  jmp(:__function_self_main__int__end)
__function_self_main__int__end:
  pop(er3) // przywracamy wartość starego `er3`
  sub(stp,1)
  ret()

// tutaj jest dodatkowo bajtkod tego `stdlib\stdio.ss`, ale jest on nieistotny

Odnośnie tego @nazwa labela:
Otóż istnieją dwa sposoby pobrania adresu labela, najczęstszy z nich to poprzez dwukropek:
:nazwa_labela - pobiera on adres labela relatywny względem aktualnego opcodu. Używany głównie (właściwie, to 'wyłącznie') podczas wywoływania call.
Oraz @nazwa_labela - pobiera on absolutny adres labela (tj.względem początku pliku). Używany np.właśnie we wskaźnikach na funkcje, jak w (bajt)kodzie powyżej.

Dodatkowo istotny jest fakt, że możliwe jest kompilowanie kodu do odpowiedników bibliotek (te biblioteki są potem linkowane statycznie wraz z aplikacją; tak na przykład zbudowane jest includowanie bibliotek standardowych - są one kompilowane wyłącznie raz i dostarczane są wraz z kompilatorem jedynie te skompilowane wersje + ich nagłówki).

I tutaj rodzi się problem:
Jeżeli na przykład napiszemy taką bibliotekę:

function<void> asdf()
{
}

public function<void> func() // tylko funkcje oznaczone jako `public` są eksportowane i widoczne poza biblioteką. "masowo" można skorzystać z makra `@visibility("public") / "private"` - tak na marginesie. Kiedyś to wszystko klarownie udokumentuję ;P
{
 var<function<void>()> f = asdf;
 f();
}

... wywołując f(); wywołamy acall na pozycję 0 (bo funkcja asdf() znajduje się na pozycji 0 w skompilowanym pliku). Niby ok, prawda? Jednak jeżeli zlinkujemy tę bibliotekę z programem i wywołamy funkcję func(), także otrzymamy skok na pozycję 0 w pliku, co już nie będzie tym, co chcemy osiągnąć (bo opcode na pozycji 0 w zlinkowanym pliku nie odpowiada opcodowi 0 w bibliotece) :P

And that's the whole problem...

Nie mam pomysłów, jakby to poprawić. Jakby potrzebne były jakieś informacje, to oczywiście podam (ciężko tak wszystko w jednym poście od razu podać).

1

Nie wiem czy cię dobrze zrozumiałem, ale czy nie chodzi po prostu o relokację?

0

Więc musiałbym zrobić listę relokowanych funkcji (labeli), zapisać ją do tego pliku-biblioteki i przy wczytywaniu (w tym wypadku przez kompilator) po prostu rozwiązywać relokacje, wstawiając od razu ten ostateczny adres funkcji (labela), dobrze myślę?

0

Tak.

0

Skoki wewnątrz funkcji też mogą ulec przesunięciu.

0

@Azarien: chyba nie rozumiem, co masz na myśli.

Aktualnie ten problem rozwiązałem w taki sposób:
Pisząc @label jako argument opcodu, jest to zapisywane jako:
0x0B + l + a + b + e + l + 0x00
(0x0B to typ argumentu, 0x00 to terminator char, bo stringi zapisywane do bajtkodu są null-terminated).
Jeżeli kompilujemy kod jako bibliotekę, zostaje to w takim formacie; a jeżeli kompilujemy już jako program, ta relokacja zostaje rozwiązana i w to miejsce wplatany zostaje właściwy adres tego labela.
Dodatkowo do biblioteki zapisywane są nazwy wszystkich labeli funkcji - informacje te są wykorzystywane przez program do ustawienia labeli w odpowiednie miejsca tak, by można było rozwiązać relokację.

A pisząc :label mamy skok w miejsce ileśtam-bajtów w tył lub przód względem aktualnego opcodu, więc niezbyt rozumiem, jak mogłoby to ulec przesunięciu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1