Ustawienie wartości tablicy inicjalizatorem

Ustawienie wartości tablicy inicjalizatorem
Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

Nie jestem pewien, czy nazwa jest odpowiednia, lecz już przedstawiam o co chodzi:

Witam!
Otóż chciałbym do mojego języka dodać obsługę konstrukcji:
var<int[]> tablica = {1, 2, 3, 4};
I stanąłem na pewnym 'problemie'. Otóż opcode służący dodawaniu pojedynczego elementu do tablicy wygląda następująco:
arset (referencja do tablicy, ilość wymiarów, wartość)
Może być to nieco niejasne, lecz na przykładzie - jeżeli chcielibyśmy wartość trzeciego indeksu tablicy, która znajduje się w rejestrze er1 ustawić na "Hello World!", prezentowałoby się to tak:

Kopiuj
push(3)
arset(er1, 1, "Hello World!")

No więc powyższy przykład to byłoby aż osiem (lub dziesięć, licząc przydzielenie tablicy pamięci) opcodów!

Kopiuj
push(0)
arset(tablica, 1, 1)

push(1)
arset(tablica, 1, 2)

push(2)
arset(tablica, 1, 3)

push(3)
arset(tablica, 1, 4)

To podejście byłoby najprostsze to implementacji, lecz wydajnościowo właściwie najgorsze.
Zatem pomyślałem nad stworzeniem opcodu w rodzaju:
arinit (referencja do tablicy, numer wymiaru, wartość indeksu 0, wartość indeksu 1, wartość indeksu 2 itd.)
Wtedy prezentowałoby się to tak:
arinit(tablica, 1, 1, 2, 3, 4)
I to byłoby najwydajniejsze - z 8 opcodów zeszliśmy do jednego, lecz z kolei problem byłby, gdyby te wartości nie były stałymi:

Kopiuj
var<int[]> tab = {a+1, b+2, c+3, d+4, e+5};
=> arinit(tablica, 1, ei1, ei2, ei3, ei4... ups, nie ma więcej rejestrów na trzymanie rvalue-ów!)

I tak źle, i tak niedobrze :/

Pytanie właściwe: w jaki najlepszy sposób można by - z poziomu VM-ki oraz kompilatora - zaimplementować ten ficzer? Nie chodzi mi koniecznie o podanie jakiegoś gotowego sposobu, tylko np.schemat, jak miałoby to wyglądać.
Póki co trzymam się jedynie tego pierwszego rozwiązania, lecz jest ono dosyć słabe...
W razie czego mogę ofc.podać więcej informacji or something, bo stoję w kropce ;c

PO
  • Rejestracja: dni
  • Ostatnio: dni
0

Te opcody wymyślałeś sam, czy używasz jakiegoś gotowego backendu kompilatora? Pytam, bo ten arinit wygląda jakoś dziwnie, bo ma zmienną liczbę argumentów. Wydaje mi się, że i tak musiałby być rozwinięty do czegoś w rodzaju tego, co przedstawiłeś w pierwszym pomyśle.

Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

Wszystko jest napisane przeze mnie, bytecode również autorski (http://sscript.4programmers.net/wiki/SSVM/Bajtkod).

Pytam, bo ten arinit wygląda jakoś dziwnie, bo ma zmienną liczbę argumentów.

Technicznie bajkod oraz VM-ka na to pozwalają, jedynie nie uwzględniłem tego, że jako np.pierwszy, drugi bądź trzeci parametr powinienem dodatkowo podać, ile tych wartości powinno być, czyli "poprawnie" wyglądałoby to tak:
arinit (referencja do tablicy, numer wymiaru, liczba wartości, wartość indeksu 0, wartość indeksu 1, wartość indeksu 2 itd.)
Jednak wciąż pozostaje problem z trzymaniem r-wartości :P

PO
  • Rejestracja: dni
  • Ostatnio: dni
1

Według mnie wymyślasz jakieś dziwne rzeczy:). Taki opcode to powinna być bardzo prosta instrukcja, prawie że zrozumiała przez procesor. Lista inicjalizacyjna tablicy
var<int[]> arr={a,b,c};to po prostu cukier syntaktyczny na

Kopiuj
var<int[3]> arr; //tablica rozmiaru 3
arr[0]=a;
arr[1]=b;
arr[2]=c;

Wystarczy to tak zamienić i gotowe. Ja bym tak zrobił. To, że masz więcej operacji, to nic nie zmienia, bo ta jedna, którą chciałeś mieć wykonywała by się tak samo długo.

Oczywiście wszystko zależy od tego, jak masz skonstruowaną swoją maszynę. Przyznam, że od dawna nie bawiłem się w kompilatory i może coś źle piszę. Może poszukaj, jak to jest zrobione w Clangu. Skompiluj kawałek kodu i sprawdź, co jest w kodzie pośrednim LLVM.

Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

Clang wyprodukował taki kod:

Kopiuj
  %tab = alloca [4 x i32], align 4
  %0 = getelementptr inbounds [4 x i32]* %tab, i32 0, i32 0
  store i32 1, i32* %0, align 4
  %1 = getelementptr inbounds [4 x i32]* %tab, i32 0, i32 1
  store i32 2, i32* %1, align 4
  %2 = getelementptr inbounds [4 x i32]* %tab, i32 0, i32 2
  store i32 3, i32* %2, align 4
  %3 = getelementptr inbounds [4 x i32]* %tab, i32 0, i32 3
  store i32 4, i32* %3, align 4

Czyli właściwie to samo, co ja chciałem zrobić na samym początku.

To, że masz więcej operacji, to nic nie zmienia, bo ta jedna, którą chciałeś mieć wykonywała by się tak samo długo.

Good point; a ja tutaj wymyślam jakieś dziwne opcody i niestworzone pseudooptymalizacje ;P

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
1

Może:

Kopiuj
push(e1)
push(e2)
...
arinit(arr, size)
Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

@winerfresh: hm, tak w gruncie rzeczy, to chyba byłoby najlepsze i najwydajniejsze rozwiązanie, nad czymś podobnym myślałem, lecz nie potrafiłem do końca wykombinować ;>

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
0

@Patryk27: chcąc nie chcąc i tak by to mniej więcej tak wyglądało, ewentualnie było by to zamknięte w jakimś opkodzie :D

Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

Niby tak, lecz:

Kopiuj
push(1)
push(2)
push(3)
push(4)
arinit(er1, 1, 4)

Będzie wydajniejsze od ręcznego ustawiania kolejnych elementów tablicy ;]

vpiotr
  • Rejestracja: dni
  • Ostatnio: dni
1

Poszedłbym krok dalej:

Kod:

Kopiuj
push(e1)
push(e2)
...
arinit(arr, size)

wykorzystuje stos (nadmiarowe majtanie bajtami).

Kod - moja wersja:

Kopiuj
arinit(arr, size)
arput(e1)
arput(e2)

wykorzustuje 1 rejestr do trzymania wskazania na tablicę i 1 rejestr na miejsce wstawiania w tablicy.

Patryk27
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 13042
0

@vpiotr: more like:

Kopiuj
arinit(array, size, out_reg)
arput(out_reg, item 1)
arput(out_reg, item 2)
...

Przy każdym wywoływaniu arput wartość rejestru out_reg zostawałaby podniesiona; natomiast nie wiem, czy nie jest już to zbyt duże przekombinowanie :P

vpiotr
  • Rejestracja: dni
  • Ostatnio: dni
0
Patryk27 napisał(a):

@vpiotr: more like:

Kopiuj
arinit(array, size, out_reg)
arput(out_reg, item 1)
arput(out_reg, item 2)
...

Przy każdym wywoływaniu arput wartość rejestru out_reg zostawałaby podniesiona; natomiast nie wiem, czy nie jest już to zbyt duże przekombinowanie :P

Moje rozwiązanie korzysta z jakiegoś ustalonego rejestru, podobnie jak w ASM / STOS.
Dzięki temu nie trzeba za każdym razem go podawać, skoro i tak przeplatanie inicjalizacji nie ma sensu.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.