Ustawienie wartości tablicy inicjalizatorem

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:

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!

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:

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

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.

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

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

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.

0

Clang wyprodukował taki kod:

  %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

1

Może:

push(e1)
push(e2)
...
arinit(arr, size)
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ć ;>

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

0

Niby tak, lecz:

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

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

1

Poszedłbym krok dalej:

Kod:

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

wykorzystuje stos (nadmiarowe majtanie bajtami).

Kod - moja wersja:

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

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

0

@vpiotr: more like:

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

0
Patryk27 napisał(a):

@vpiotr: more like:

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.

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