Obsah
1. Překlad jazykové konstrukce goto
2. Podmínky a instrukce select
5. Přečtení hodnoty prvku z pole
6. Modifikace hodnoty prvku v poli
7. Modifikace prvků v poli přes ukazatel
8. Instrukce LLVM určené pro manipulaci s prvky pole
10. Způsob překladu bez použití vektorových instrukcí
11. Doplnění instrukčního souboru: co nebylo popsáno minule
12. Bitové operace součtu, součinu a exkluzivního součtu
14. Operace s hodnotami s plovoucí řádovou čárkou s poloviční přesností
15. Aritmetické operace s hodnotami typu half float
16. Porovnání hodnot typu half float
17. Příloha A: seznam doposud popsaných instrukcí používaných v LLVM IR
18. Příloha B: Makefile soubor pro překlad všech demonstračních příkladů
19. Repositář s demonstračními příklady
1. Překlad jazykové konstrukce goto
V prvním příkladu, který si v dnešním článku ukážeme, prozkoumáme, jakou sekvencí instrukcí mezikódu je realizována konstrukce goto. Jedná se o nepodmíněný skok, takže se dá předpokládat, že v LLMV IR bude použita „univerzální“ instrukce br (branch):
void positive_case(void);
void negative_case(void);
void finish(void);
void bar(int i) {
if (i > 0) {
goto positive;
}
positive_case();
goto end;
positive:
negative_case();
end:
finish();
}
Překlad do mezikódu LLVM IR dopadne následovně:
; Function Attrs: nounwind optsize uwtable
define dso_local void @bar(i32 noundef %0) local_unnamed_addr #0 {
%2 = icmp sgt i32 %0, 0
br i1 %2, label %4, label %3
3: ; preds = %1
tail call void @positive_case() #2
br label %5
4: ; preds = %1
tail call void @negative_case() #2
br label %5
5: ; preds = %4, %3
tail call void @finish() #2
ret void
}
2. Podmínky a instrukce select
Ve skutečnosti se v mezikódu LLVM IR můžeme setkat ještě s jednou zajímavou instrukcí. Tato instrukce se jmenuje select a slouží nikoli k rozvětvení na základě podmínky, ale k výběru hodnoty, taktéž na základě podmínky. Ukažme si jednoduchý příklad, který v LLVM IR vede k použití této instrukce:
int decision(int a) {
if (a < 0) {
return 42;
}
return 9999;
}
Teoreticky by bylo možné tuto funkci přeložit s využitím podmíněného skoku atd., ovšem v mezikódu namísto toho nalezneme právě instrukci select. Povšimněte si, jak se používá – na základě podmínky (první operand) se vybere buď hodnota druhá nebo třetí. Všechny hodnoty jsou v LLVM IR doplněny o typy:
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef range(i32 42, 10000) i32 @decision(i32 noundef %0) local_unnamed_addr #0 {
%2 = icmp slt i32 %0, 0
%3 = select i1 %2, i32 42, i32 9999
ret i32 %3
}
Naši tabulku instrukcí LLVM IR tedy rozšíříme o:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | select | podmínka, hodnota1, hodnota2 | výběr hodnoty na základě vyhodnocené podmínky |
3. Rekurzivní volání funkcí
V LLVM IR je umožněno, aby nějaká funkce volala sebe samu. Pro tento účel se používá již popsaná instrukce call popř. její „koncová“ varianta tail call. Ukažme si celý postup na známé Ackermannově funkci, která není primitivně rekurzivní:
unsigned int ackermann(unsigned int m, unsigned int n) {
if (m == 0)
return n + 1;
else if (n == 0)
return ackermann(m - 1, 1);
else
return ackermann(m - 1, ackermann(m, n - 1));
}
Výsledek překladu do mezikódu LLVM IR dopadne následovně. Připomínám, že jsou povoleny optimalizace na velikost výsledného kódu:
; Function Attrs: nofree nosync nounwind optsize memory(none) uwtable
define dso_local i32 @ackermann(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = icmp eq i32 %0, 0
br i1 %3, label %4, label %7
4: ; preds = %11, %2
%5 = phi i32 [ %1, %2 ], [ %12, %11 ]
%6 = add i32 %5, 1
ret i32 %6
7: ; preds = %2, %11
%8 = phi i32 [ %12, %11 ], [ %1, %2 ]
%9 = phi i32 [ %13, %11 ], [ %0, %2 ]
%10 = icmp eq i32 %8, 0
br i1 %10, label %11, label %15
11: ; preds = %7, %15
%12 = phi i32 [ %17, %15 ], [ 1, %7 ]
%13 = add i32 %9, -1
%14 = icmp eq i32 %13, 0
br i1 %14, label %4, label %7
15: ; preds = %7
%16 = add i32 %8, -1
%17 = tail call i32 @ackermann(i32 noundef %9, i32 noundef %16) #1
br label %11
}
4. Manipulace s poli
Mezikód LLVM IR obsahuje i instrukce (resp. přesněji řečeno jednu instrukci) pro manipulaci s poli (array). Od mezikódu vyžadujeme dvě operace: přečtení i-tého prvku pole a zápis do i-tého prvku pole, samozřejmě za předpokladu, že je pole měnitelné (mutable). Tyto operace sice přímo podporovány nejsou, ovšem v LLVM IR je k dispozici instrukce, která na základě ukazatele na pole a indexu prvku vypočte a vrátí ukazatel na tento prvek. To je plně dostačující, protože čtení a zápisy přes ukazatel již známe – k tomuto účelu se používají instrukce load a store, které jsme si popsali již předminule v osmé kapitole. V navazujících kapitolách si ukážeme, jak se do LLVM IR přeloží operace s poli prováděné buď indexováním prvků nebo přístupem k prvkům přes ukazatel.
5. Přečtení hodnoty prvku z pole
První (a dosti základní) operací, která se s poli provádí, je přečtení hodnoty prvku pole, přičemž prvek je identifikován svým indexem. Programovací jazyk C přitom žádným způsobem nekontroluje meze indexů. Můžeme tedy naprogramovat obecnou funkci, která z pole hodnot typu celé číslo vrátí prvek s indexem index:
unsigned int set(unsigned int *array, int index) {
return array[index];
}
Překlad do mezikódu LLVM IR bude vypadat takto:
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(argmem: read) uwtable
define dso_local i32 @set(ptr nocapture noundef readonly %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = sext i32 %1 to i64
%4 = getelementptr inbounds i32, ptr %0, i64 %3
%5 = load i32, ptr %4, align 4, !tbaa !3
ret i32 %5
}
V mezikódu se nejdříve 32bitový index rozšíří na 64 bitů (se znaménkem, takže se korektně řeší i záporné indexy) a dále se zavolá instrukce nazvaná getelementptr. U této instrukce je specifikován typ prvků pole, adresa pole (což je vlastně adresa prvního prvku v poli) a 64bitový index prvku. Výsledkem je ukazatel na tento prvek, takže bylo možné pokračovat instrukcí load, kterou již dobře známe.
6. Modifikace hodnoty prvku v poli
Druhou operací, kterou musí LLVM IR při práci s poli nějakým způsobem podporovat, je modifikace (zápis) prvku pole; samozřejmě za předpokladu, že je pole modifikovatelné. Vyzkoušejme si, jakým způsobem Clang přeloží následující funkci, která nastaví všechny prvky pole (resp. size prvků) na hodnotu value. První implementace algoritmu používá při přístupu k prvkům pole běžné indexování:
void set(unsigned int *array, int size, unsigned int value) {
int i;
for (i = 0; i < size; i++) {
array[i] = value;
}
}
Po překladu tohoto demonstračního příkladu do mezikódu LLVM IR je opět patrné, že se použila instrukce getelementptr. Zápis prvků pole je proveden ve smyčce, i když LLVM IR podporuje i vektorové operace (s těmi se podrobněji seznámíme příště):
; Function Attrs: nofree norecurse nosync nounwind optsize memory(argmem: write) uwtable
define dso_local void @set(ptr nocapture noundef writeonly %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 {
%4 = icmp sgt i32 %1, 0
br i1 %4, label %5, label %12
5: ; preds = %3
%6 = zext nneg i32 %1 to i64
br label %7
7: ; preds = %5, %7
%8 = phi i64 [ 0, %5 ], [ %10, %7 ]
%9 = getelementptr inbounds nuw i32, ptr %0, i64 %8
store i32 %2, ptr %9, align 4, !tbaa !3
%10 = add nuw nsw i64 %8, 1
%11 = icmp eq i64 %10, %6
br i1 %11, label %12, label %7, !llvm.loop !7
12: ; preds = %7, %3
ret void
}
7. Modifikace prvků v poli přes ukazatel
Demonstrační příklad z předchozí kapitoly můžeme snadno upravit tak, aby se namísto operátoru indexování prováděl zápis přes ukazatel. V dávných dobách se jednalo o rychlejší řešení a proto bude zajímavé zjistit, jak se změní LLVM IR pro upravenou funkci set:
void set(unsigned int *array, int size, unsigned int value) {
unsigned int *p = array;
int i;
for (i = 0; i < size; i++) {
*p++ = value;
}
}
Výsledek překladu do bajtkódu:
; Function Attrs: nofree norecurse nosync nounwind optsize memory(argmem: write) uwtable
define dso_local void @set(ptr nocapture noundef writeonly %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 {
%4 = icmp sgt i32 %1, 0
br i1 %4, label %5, label %11
5: ; preds = %3, %5
%6 = phi i32 [ %9, %5 ], [ 0, %3 ]
%7 = phi ptr [ %8, %5 ], [ %0, %3 ]
%8 = getelementptr inbounds nuw i8, ptr %7, i64 4
store i32 %2, ptr %7, align 4, !tbaa !3
%9 = add nuw nsw i32 %6, 1
%10 = icmp eq i32 %9, %1
br i1 %10, label %11, label %5, !llvm.loop !7
11: ; preds = %5, %3
ret void
}
Výsledek je skutečně nepatrně kratší, protože chybí konverze indexu na 64bitovou hodnotu a přímý skok na začátek smyčky (ten by ovšem byl stejně odstraněn v dalších krocích optimalizace):
%4 = icmp sgt i32 %1, 0 %4 = icmp sgt i32 %1, 0 br i1 %4, label %5, label %12 br i1 %4, label %5, label %11 5: 5: %6 = zext nneg i32 %1 to i64 br label %7 %6 = phi i32 [ %9, %5 ], [ 0, %3 ] 7: %7 = phi ptr [ %8, %5 ], [ %0, %3 ] %8 = phi i64 [ 0, %5 ], [ %10, %7 ] %9 = getelementptr inbounds nuw i32, ptr %0, i64 %8 %8 = getelementptr inbounds nuw i8, ptr %7, i64 4 store i32 %2, ptr %9, align 4, !tbaa !3 store i32 %2, ptr %7, align 4, !tbaa !3 %10 = add nuw nsw i64 %8, 1 %9 = add nuw nsw i32 %6, 1 %11 = icmp eq i64 %10, %6 %10 = icmp eq i32 %9, %1 br i1 %11, label %12, label %7, !llvm.loop !7 br i1 %10, label %11, label %5, !llvm.loop !7 12: 11: ret void ret void
8. Instrukce LLVM určené pro manipulaci s prvky pole
Ve všech třech demonstračních příkladech, která četly nebo zapisovaly prvky do pole, se používala jediná nová instrukce getelementptr:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | getelementptr | typ prvků, adresa pole, index prvku | adresa i-tého prvku v poli |
Typické použití této instrukce při čtení hodnoty prvku:
%x = getelementptr inbounds typ, ukazatel, index %y = load typ, ptr %x
Typické použití instrukce při modifikaci prvku:
%x = getelementptr inbounds typ, ukazatel, index store typ hodnota, ptr ukazatel
9. Výpočet součtu prvků pole
Ještě se pro zajímavost podívejme na způsob překladu funkcí, které provedou součet size prvků v poli. První implementace výpočtu bude opět používat operátor indexování:
unsigned int sum(unsigned int *array, int size) {
unsigned int s = 0;
int i;
for (i = 0; i < size; i++) {
s += array[i];
}
return s;
}
Druhá varianta téže funkce naproti tomu přistupuje k prvkům přes ukazatel:
unsigned int sum(unsigned int *array, int size) {
unsigned int s = 0;
unsigned int *p = array;
int i;
for (i = 0; i < size; i++) {
s += *p++;
}
return s;
}
10. Způsob překladu bez použití vektorových instrukcí
Překlad obou demonstračních příkladů z předchozí kapitoly provedeme s vypnutím „vektorizaci“, což se konkrétně na platformě x86(64) provádí přepínačem -mno-sse. Z výsledků je opět patrné použití výše uvedené instrukce getelementptr:
; Function Attrs: nofree norecurse nosync nounwind optsize memory(argmem: read) uwtable
define dso_local i32 @sum(ptr nocapture noundef readonly %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = icmp sgt i32 %1, 0
br i1 %3, label %4, label %14
4: ; preds = %2
%5 = zext nneg i32 %1 to i64
br label %6
6: ; preds = %4, %6
%7 = phi i64 [ 0, %4 ], [ %12, %6 ]
%8 = phi i32 [ 0, %4 ], [ %11, %6 ]
%9 = getelementptr inbounds nuw i32, ptr %0, i64 %7
%10 = load i32, ptr %9, align 4, !tbaa !3
%11 = add i32 %10, %8
%12 = add nuw nsw i64 %7, 1
%13 = icmp eq i64 %12, %5
br i1 %13, label %14, label %6, !llvm.loop !7
14: ; preds = %6, %2
%15 = phi i32 [ 0, %2 ], [ %11, %6 ]
ret i32 %15
}
Překlad varianty postavené na přístupu k prvkům polí přes ukazatel:
; Function Attrs: nofree norecurse nosync nounwind optsize memory(argmem: read) uwtable
define dso_local i32 @sum(ptr nocapture noundef readonly %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = icmp sgt i32 %1, 0
br i1 %3, label %4, label %13
4: ; preds = %2, %4
%5 = phi i32 [ %11, %4 ], [ 0, %2 ]
%6 = phi ptr [ %8, %4 ], [ %0, %2 ]
%7 = phi i32 [ %10, %4 ], [ 0, %2 ]
%8 = getelementptr inbounds nuw i8, ptr %6, i64 4
%9 = load i32, ptr %6, align 4, !tbaa !3
%10 = add i32 %9, %7
%11 = add nuw nsw i32 %5, 1
%12 = icmp eq i32 %11, %1
br i1 %12, label %13, label %4, !llvm.loop !7
13: ; preds = %4, %2
%14 = phi i32 [ 0, %2 ], [ %10, %4 ]
ret i32 %14
}
Porovnání po instrukcích:
%3 = icmp sgt i32 %1, 0 %3 = icmp sgt i32 %1, 0
br i1 %3, label %4, label %14 br i1 %3, label %4, label %13
4: 4:
%5 = zext nneg i32 %1 to i64
br label %6
6:
%7 = phi i64 [ 0, %4 ], [ %12, %6 ] %5 = phi i32 [ %11, %4 ], [ 0, %2 ]
%8 = phi i32 [ 0, %4 ], [ %11, %6 ] %6 = phi ptr [ %8, %4 ], [ %0, %2 ]
%7 = phi i32 [ %10, %4 ], [ 0, %2 ]
%9 = getelementptr inbounds nuw i32, ptr %0, i64 %7 %8 = getelementptr inbounds nuw i8, ptr %6, i64 4
%10 = load i32, ptr %9, align 4, !tbaa !3 %9 = load i32, ptr %6, align 4, !tbaa !3
%11 = add i32 %10, %8 %10 = add i32 %9, %7
%12 = add nuw nsw i64 %7, 1 %11 = add nuw nsw i32 %5, 1
%13 = icmp eq i64 %12, %5 %12 = icmp eq i32 %11, %1
br i1 %13, label %14, label %6, !llvm.loop !7 br i1 %12, label %13, label %4, !llvm.loop !7
14: 13:
%15 = phi i32 [ 0, %2 ], [ %11, %6 ] %14 = phi i32 [ 0, %2 ], [ %10, %4 ]
ret i32 %15 ret i32 %14
11. Doplnění instrukčního souboru: co nebylo popsáno minule
V navazujícím článku se budeme zabývat vektorovými (SIMD) instrukcemi, které LLVM IR taktéž podporuje. Ovšem nejprve musíme dokončit popis „skalárních“ instrukcí, přesněji řečeno instrukcí, které pracují se skalárními operandy. Většinu instrukcí (alespoň těch, které jsou vztaženy k jazyku C) jsme si již popsali, ovšem ještě nám zbývá popis bitových operací, výpočet zbytku po dělení a v neposlední řadě taktéž zpracování hodnot s plovoucí řádovou čárkou s poloviční přesností (half floating point).
12. Bitové operace součtu, součinu a exkluzivního součtu
Programovací jazyk C podporuje provádění bitových operací, což znamená, že tyto operace musí být nějakým způsobem reprezentovány i v mezikódu LLVM IR. Ostatně není nic snadnějšího, než si tyto operace otestovat. Opět použijeme příklad postavený nad makrosystémem jazyka C:
#define AND(type) type and_##type(type x, type y) {return x&y;}
#define OR(type) type or_##type(type x, type y) {return x|y;}
#define XOR(type) type xor_##type(type x, type y) {return x^y;}
#define ALL(type) \
AND(type) \
OR(type) \
XOR(type)
ALL(int)
ALL(unsigned)
Po expanzi všech maker získáme tento zdrojový kód:
int and_int(int x, int y) {return x&y;}
int or_int(int x, int y) {return x|y;}
int xor_int(int x, int y) {return x^y;}
unsigned and_unsigned(unsigned x, unsigned y) {return x&y;}
unsigned or_unsigned(unsigned x, unsigned y) {return x|y;}
unsigned xor_unsigned(unsigned x, unsigned y) {return x^y;}
A po překladu do mezikódu LLVM IR můžeme vidět použití nových instrukcí (plně typovaných):
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @and_int(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = and i32 %1, %0
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @or_int(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = or i32 %1, %0
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @xor_int(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = xor i32 %1, %0
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @and_unsigned(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = and i32 %1, %0
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @or_unsigned(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = or i32 %1, %0
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @xor_unsigned(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = xor i32 %1, %0
ret i32 %3
}
Všechny tři nové instrukce LLVM IR jsou vypsány v tabulce:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | and | celočíselný parametr, celočíselný parametr | bitový součin dvou celočíselných hodnot |
| 2 | or | celočíselný parametr, celočíselný parametr | bitový součet dvou celočíselných hodnot |
| 3 | xor | celočíselný parametr, celočíselný parametr | bitová nonekvivalence dvou celočíselných hodnot |
13. Výpočet zbytku po dělení
Při popisu aritmetických operací jsme opomenuli výpočet zbytku po dělení (resp. operaci modulo – což pro záporné operandy nemusí být totéž). Doplníme si tedy i znalosti o této operaci a o způsobu jejího překladu do LLVM IR:
#define REM(type) type remainder_##type(type x, type y) {return x%y;}
#define ALL(type) \
REM(type)
ALL(int)
ALL(unsigned)
Výsledek expanze maker:
int remainder_int(int x, int y) {return x%y;}
unsigned remainder_unsigned(unsigned x, unsigned y) {return x%y;}
Překlad do mezikódu LLVM IR:
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 -2147483647, -2147483648) i32 @remainder_int(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = srem i32 %0, %1
ret i32 %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, -1) i32 @remainder_unsigned(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
%3 = urem i32 %0, %1
ret i32 %3
}
Máme tedy dvě nové instrukce, ke kterým ještě přidám instrukci frem, kterou lze v C těžko „vyrobit“:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | srem | hodnota1, hodnota2 | operace modulo pro operandy se znaménkem |
| 2 | urem | hodnota1, hodnota2 | operace modulo pro operandy bez znaménka |
| 3 | frem | hodnota1, hodnota2 | operace modulo pro operandy s plovoucí řádovou čárkou |
14. Operace s hodnotami s plovoucí řádovou čárkou s poloviční přesností
Programátoři, kteří se zabývají vývojem v oblasti generativní AI, zpracování přirozeného jazyka, embeddingu a embedding modelů atd., se mohou setkat s nutností zpracování skalárních hodnot či celých vektorů s prvky typu half float (FP16). Důvody jsou zřejmé: pro mnoho účelů rozsah i přesnost hodnot uložených jako half float plně vyhovuje požadavkům řešeného algoritmu a oproti hodnotám single jsou paměťové požadavky poloviční (a v praxi je velký finanční rozdíl v tom, jestli servery vyžadují například 16GB RAM nebo 32GB RAM).
Formát half float, jenž je dnes standardizován v IEEE 754–2008, používá pro ukládání hodnot s plovoucí řádovou čárkou pouhých šestnáct bitů, tj. dva byty. Maximální hodnota je rovna 65504, minimální hodnota (větší než nula) přibližně 5,9×10-8. Předností tohoto formátu je malá bitová šířka (umožňuje paralelní přenos po interních sběrnicích GPU) a také větší rychlost zpracování základních operací, protože pro tak malou bitovou šířku mantisy je možné některé operace „zadrátovat“ a nepočítat pomocí ALU. Také některé iterativní výpočty (sin, cos, sqrt) mohou být provedeny rychleji, než v případě plnohodnotných typů float a single.
| Celkový počet bitů (bytů): | 16 (2) |
| Bitů pro znaménko: | 1 |
| Bitů pro exponent: | 5 |
| Bitů pro mantisu: | 10 |
| BIAS (offset exponentu): | 15 |
| Přesnost: | 5–6 číslic |
| Maximální hodnota: | 65504 |
| Minimální hodnota: | –65504 |
| Nejmenší kladná nenulová hodnota: | 5,96×10-8 |
| Nejmenší kladná normalizovaná hodnota: | 6,104×10-5 |
| Podpora záporné nuly: | ano |
| Podpora +∞: | ano |
| Podpora -∞: | ano |
| Podpora NaN: | ano |
15. Aritmetické operace s hodnotami typu half float
Clang a současně i LLVM IR hodnoty typu half float podporují. Pokusme se tedy zjistit, jaké se vlastně v LLVM IR pro tento účel používají instrukce. Nejprve se zaměříme na základní aritmetické operace, které otestujeme tímto kódem:
#define NEG(type) type neg_##type(type x) {return -x;}
#define ADD(type) type add_##type(type x, type y) {return x+y;}
#define SUB(type) type sub_##type(type x, type y) {return x-y;}
#define MUL(type) type mul_##type(type x, type y) {return x*y;}
#define DIV(type) type div_##type(type x, type y) {return x/y;}
#define ALL(type) \
NEG(type) \
ADD(type) \
SUB(type) \
MUL(type) \
DIV(type)
ALL(_Float16)
Po expanzi maker preprocesorem získáme tuto pětici funkcí:
_Float16 neg__Float16(_Float16 x) {return -x;}
_Float16 add__Float16(_Float16 x, _Float16 y) {return x+y;}
_Float16 sub__Float16(_Float16 x, _Float16 y) {return x-y;}
_Float16 mul__Float16(_Float16 x, _Float16 y) {return x*y;}
_Float16 div__Float16(_Float16 x, _Float16 y) {return x/y;}
Výsledek překladu do mezikódu LLVM IR:
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef half @neg__Float16(half noundef %0) local_unnamed_addr #0 {
%2 = fneg half %0
ret half %2
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef half @add__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fadd half %0, %1
ret half %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef half @sub__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fsub half %0, %1
ret half %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef half @mul__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fmul half %0, %1
ret half %3
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef half @div__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fdiv half %0, %1
ret half %3
}
Z tohoto výpisu je patrné, že se používají již známé instrukce pro práci s hodnotami s plovoucí řádovou čárkou. Pouze se změnil identifikátor typu (modifikátor) zapsaný za jménem instrukce:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | fneg | float/double | změna znaménka |
| 2 | fadd | hodnota1, hodnota2 | součet dvou hodnot s plovoucí řádovou čárkou |
| 3 | fsub | hodnota1, hodnota2 | rozdíl dvou hodnot s plovoucí řádovou čárkou |
| 4 | fmul | hodnota1, hodnota2 | součin dvou hodnot s plovoucí řádovou čárkou |
| 5 | fdiv | hodnota1, hodnota2 | podíl dvou hodnot s plovoucí řádovou čárkou |
16. Porovnání hodnot typu half float
Podobným způsobem můžeme prozkoumat instrukce použité při porovnání dvou hodnot typu half float. K tomuto účelu opět využijeme variantu příkladu z předchozího článku:
#define EQ(type) int eq_##type(type x, type y) {return x==y;}
#define NE(type) int ne_##type(type x, type y) {return x!=y;}
#define LT(type) int lt_##type(type x, type y) {return x<y;}
#define LE(type) int le_##type(type x, type y) {return x<=y;}
#define GT(type) int gt_##type(type x, type y) {return x>y;}
#define GE(type) int ge_##type(type x, type y) {return x>=y;}
#define ALL(type) \
EQ(type) \
NE(type) \
LT(type) \
LE(type) \
GT(type) \
GE(type)
ALL(_Float16)
Výsledek činnosti preprocesoru jazyka C:
int eq__Float16(_Float16 x, _Float16 y) {return x==y;}
int ne__Float16(_Float16 x, _Float16 y) {return x!=y;}
int lt__Float16(_Float16 x, _Float16 y) {return x<y;}
int le__Float16(_Float16 x, _Float16 y) {return x<=y;}
int gt__Float16(_Float16 x, _Float16 y) {return x>y;}
int ge__Float16(_Float16 x, _Float16 y) {return x>=y;}
Překlad do LLVM IR:
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @eq__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp oeq half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @ne__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp une half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @lt__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp olt half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @le__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp ole half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @gt__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp ogt half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @ge__Float16(half noundef %0, half noundef %1) local_unnamed_addr #0 {
%3 = fcmp oge half %0, %1
%4 = zext i1 %3 to i32
ret i32 %4
}
Opět je patrné, že se používá již známá instrukce fcmp, ovšem s odlišným modifikátorem typu:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | fcmp oeq | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „rovno“ |
| 2 | fcmp une | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „nerovno“ |
| 3 | fcmp olt | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší než“ |
| 4 | fcmp ole | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší nebo rovno“ |
| 5 | fcmp ogt | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší než“ |
| 6 | fcmp oge | half float, half float | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší nebo rovno“ |
17. Příloha A: seznam doposud popsaných instrukcí používaných v LLVM IR
V tabulce vypsané pod tímto odstavcem jsou vypsány všechny doposud popsané a použité instrukce LLVM IR:
| # | Jméno instrukce | Parametry | Stručný popis instrukce |
|---|---|---|---|
| 1 | alloca | typ (velikost) | alokace místa na zásobníku |
| 2 | load | adresa | načtení hodnoty z paměti |
| 3 | store | hodnota, adresa | uložení hodnoty do paměti |
| 4 | ret | hodnota | návrat z funkce |
| 5 | add | hodnota1, hodnota2 | součet dvou celočíselných hodnot |
| 6 | sub | hodnota1, hodnota2 | rozdíl dvou celočíselných hodnot |
| 7 | mul | hodnota1, hodnota2 | součin dvou celočíselných hodnot |
| 8 | sdiv | hodnota1, hodnota2 | podíl dvou celočíselných hodnot, které mají znaménko (signed) |
| 9 | udiv | hodnota1, hodnota2 | podíl dvou celočíselných hodnot, které jsou bezznaménkové (unsigned) |
| 10 | srem | hodnota1, hodnota2 | operace modulo pro operandy se znaménkem |
| 11 | urem | hodnota1, hodnota2 | operace modulo pro operandy bez znaménka |
| 12 | and | celočíselný parametr, celočíselný parametr | bitový součin dvou celočíselných hodnot |
| 13 | or | celočíselný parametr, celočíselný parametr | bitový součet dvou celočíselných hodnot |
| 14 | xor | celočíselný parametr, celočíselný parametr | bitová nonekvivalence dvou celočíselných hodnot |
| 15 | shl | hodnota, posun | aritmetický či bitový posun doleva |
| 16 | ashr | hodnota, posun | aritmetický posun doprava |
| 17 | lshr | hodnota, posun | bitový posun doprava |
| 18 | icmp eq | hodnota1, hodnota2 | porovnání dvou operandů na relaci „rovno“ |
| 19 | icmp ne | hodnota1, hodnota2 | porovnání dvou operandů na relaci „nerovno“ |
| 20 | icmp slt | hodnota1, hodnota2 | porovnání dvou operandů se znaménkem na relaci „menší než“ |
| 21 | icmp sle | hodnota1, hodnota2 | porovnání dvou operandů se znaménkem na relaci „menší nebo rovno“ |
| 22 | icmp sgt | hodnota1, hodnota2 | porovnání dvou operandů se znaménkem na relaci „větší než“ |
| 23 | icmp sge | hodnota1, hodnota2 | porovnání dvou operandů se znaménkem na relaci „větší nebo rovno“ |
| 24 | icmp ult | hodnota1, hodnota2 | porovnání dvou operandů bez znaménka na relaci „menší než“ |
| 25 | icmp ule | hodnota1, hodnota2 | porovnání dvou operandů bez znaménka na relaci „menší nebo rovno“ |
| 26 | icmp ugt | hodnota1, hodnota2 | porovnání dvou operandů bez znaménka na relaci „větší než“ |
| 27 | icmp uge | hodnota1, hodnota2 | porovnání dvou operandů bez znaménka na relaci „větší nebo rovno“ |
| 28 | fneg | float/double | změna znaménka hodnoty s plovoucí řádovou čárkou |
| 29 | fadd | hodnota1, hodnota2 | součet dvou hodnot s plovoucí řádovou čárkou |
| 30 | fsub | hodnota1, hodnota2 | rozdíl dvou hodnot s plovoucí řádovou čárkou |
| 31 | fmul | hodnota1, hodnota2 | součin dvou hodnot s plovoucí řádovou čárkou |
| 32 | fdiv | hodnota1, hodnota2 | podíl dvou hodnot s plovoucí řádovou čárkou |
| 33 | frem | hodnota1, hodnota2 | operace modulo pro operandy s plovoucí řádovou čárkou |
| 34 | fcmp oeq | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „rovno“ |
| 35 | fcmp une | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „nerovno“ |
| 36 | fcmp olt | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší než“ |
| 37 | fcmp ole | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší nebo rovno“ |
| 38 | fcmp ogt | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší než“ |
| 39 | fcmp oge | float/double, float/double | porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší nebo rovno“ |
| 40 | zext | typ1, operand, typ2 | bezznaménkové rozšíření z typu1 na typ2 |
| 41 | sext | typ1, operand, typ2 | znaménkové rozšíření z typu1 na typ2 |
| 42 | trunc | typ1, operand, typ2 | zmenšení bitové šířky z typu1 na typ2 |
| 43 | fpext | typ1, hodnota, typ2 | převod FP hodnoty z typu 1 na typ 2 (rozšíření přesnosti/rozsahu) |
| 44 | fptrunc | typ1, hodnota, typ2 | převod FP hodnoty z typu 1 na typ 2 (zmenšení přesnosti/rozsahu) |
| 45 | sitofp | typ1, hodnota, typ2 | převod celočíselné hodnoty se znaménkem na FP hodnotu |
| 46 | uitofp | typ1, hodnota, typ2 | převod celočíselné hodnoty bez znaménka na FP hodnotu |
| 46 | fptosi | typ1, hodnota, typ2 | převod FP hodnoty na celočíselnou hodnotu se znaménkem (signed) |
| 47 | fptoui | typ1, hodnota, typ2 | převod FP hodnoty na celočíselnou hodnotu bez znaménka (unsigned) |
| 48 | br | label číslo | nepodmíněný skok na nastavené návěští |
| 49 | br | podmínka label číslo | podmíněný skok na nastavené návěští |
| 50 | switch | rozeskok na základě tabulky s dvojicemi hodnota:návěští | |
| 51 | phi | typ [hodnota, hodnota] … | instrukce použitá při konverzi mezi LLVM IR a SSA |
| 52 | select | podmínka, hodnota1, hodnota2 | výběr hodnoty na základě vyhodnocené podmínky |
| 53 | getelementptr | typ prvků, adresa pole, index prvku | adresa i-tého prvku v poli |
18. Příloha B: Makefile soubor pro překlad všech demonstračních příkladů
Všechny demonstrační příklady využívající překladač Clang, které byly použity v dnešním článku i v obou článcích předchozích, je možné přeložit do mezijazyka LLVM IR s využitím souboru Makefile, jehož obsah je vypsán pod tímto odstavcem:
CC=clang
outputs := noop.ll noop_no_opt.ll noop_wasm.ll noop_aarch64.ll \
add_unsigned.ll add_unsigned_no_opt.ll \
add_signed.ll numeric_types.ll \
arith_operators.ll arith_operators_float.ll arith_operators_fp16.ll \
comparison_operators.ll comparison_operators_float.ll comparison_operators_fp16.ll \
branching_1.ll branching_2.ll pointers_1.ll pointers_2.ll \
add_arrays_1.ll add_arrays_2.ll \
fibonacci.ll \
numeric_conversions_1.ll numeric_conversions_2.ll numeric_conversions_3.ll \
switch_1.ll switch_2.ll switch_3.ll switch_4.ll switch_5.ll switch_6.ll \
array_set_int_1.ll array_set_int_2.ll array_get_int.ll \
array_sum_int_1.ll array_sum_int_2.ll \
bitwise_operators.ll remainder.ll \
goto.ll stack.ll select.ll
all: $(outputs)
clean:
rm -f *.ll
.PHONY: all clean
%.ll: %.c
$(CC) -Os -S -emit-llvm $< -o $@
add_unsigned_no_opt.ll: add_unsigned.c
$(CC) -S -emit-llvm $< -o $@
noop_no_opt.ll: noop.c
$(CC) -S -emit-llvm $< -o $@
noop_wasm.ll: noop.c
$(CC) -Os -S -emit-llvm --target=wasm32 $< -o $@
noop_aarch64.ll: noop.c
$(CC) -Os -S -emit-llvm --target=aarch64 $< -o $@
array_set_int_1.ll: array_set_int_1.c
$(CC) -Os -S -mno-sse -emit-llvm $< -o $@
array_set_int_2.ll: array_set_int_2.c
$(CC) -Os -S -mno-sse -emit-llvm $< -o $@
array_sum_int_1.ll: array_sum_int_1.c
$(CC) -Os -S -mno-sse -emit-llvm $< -o $@
array_sum_int_2.ll: array_sum_int_2.c
$(CC) -Os -S -mno-sse -emit-llvm $< -o $@
Pro překlad všech demonstračních příkladů postačuje zadat příkaz:
$ make
Pro smazání všech vytvářených souborů s LLVM IR (mají koncovku .ll) použijte příkaz:
$ make clean
19. Repositář s demonstračními příklady
Demonstrační příklady, s nimiž jsme se v předchozích článcích i v dnešním článku seznámili a které jsou určeny pro překlad s využitím Clangu, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy psané v jazyku C i soubory v mezijazyku LLVM IR získané překladem Clangem:
| # | Příklad | Stručný popis příkladu | Adresa |
|---|---|---|---|
| 1 | Makefile | definice cílů pro překlad všech demonstračních příkladů z této tabulky | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/Makefile |
| 2 | noop.c | prázdná funkce bez parametrů nevracející žádnou hodnotu | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/noop.c |
| 3 | noop_no_opt.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka bez provádění optimalizací | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/noop_no_opt.ll |
| 4 | noop.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka s provedením optimalizací na velikost | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/noop.ll |
| 5 | noop_aarch64.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka se specifikami platformy AArch64 | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/noop_aarch64.ll |
| 6 | noop_wasm.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka se specifikami platformy WebAssembly | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/noop_wasm.ll |
| 7 | add_unsigned.c | funkce pro součet dvou celých čísel bez znaménka (unsigned) | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_unsigned.c |
| 8 | add_unsigned_no_opt.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka bez provedení optimalizací | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_unsigned_no_opt.ll |
| 9 | add_unsigned.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka s provedením optimalizací na velikost | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_unsigned.ll |
| 10 | add_signed.c | funkce pro výpočet dvou celých čísel se znaménkem (signed) | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_signed.c |
| 11 | add_signed.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_signed.ll |
| 12 | arith_operators.c | aritmetické operátory programovacího jazyka C | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators.c |
| 13 | arith_operators.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators.ll |
| 14 | numeric_types.c | práce se základními numerickými datovými typy v jazyku C | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_types.c |
| 15 | numeric_types.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_types.ll |
| 16 | comparison_operators.c | relační operátory programovacího jazyka C použité ve výrazech | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators.c |
| 17 | comparison_operators.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators.ll |
| 18 | pointers1.c | základní práce s ukazateli v programovacím jazyku C, první demonstrační příklad | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/pointers1.c |
| 19 | pointers1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/pointers1.ll |
| 20 | pointers2.c | základní práce s ukazateli v programovacím jazyku C, druhý demonstrační příklad | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/pointers2.c |
| 21 | pointers2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/pointers2.ll |
| 22 | branching1.c | rozeskoky vzniklé překladem podmíněných konstrukcí a programových smyček, první demonstrační příklad | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/branching1.c |
| 23 | branching1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/branching1.ll |
| 24 | branching2.c | rozeskoky vzniklé překladem podmíněných konstrukcí a programových smyček, druhý demonstrační příklad | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/branching2.c |
| 25 | branching2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/branching2.ll |
| 26 | add_arrays1.c | součet prvků polí, předání polí odkazem | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_arrays1.c |
| 27 | add_arrays1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_arrays1.ll |
| 28 | add_arrays2.c | součet prvků polí, specifikace aliasingu předávaných odkazů | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_arrays2.c |
| 29 | add_arrays2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/add_arrays2.ll |
| 30 | fibonacci.c | výpočet Fibonacciho posloupnosti | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/fibonacci.c |
| 31 | fibonacci.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/fibonacci.ll |
| 32 | numeric_conversions1.c | numerické konverze mezi celočíselnými typy bez znaménka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions1.c |
| 33 | numeric_conversions1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions1.ll |
| 34 | numeric_conversions2.c | numerické konverze mezi celočíselnými typy se znaménkem | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions2.c |
| 35 | numeric_conversions2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions2.ll |
| 36 | numeric_conversions3.c | numerické konverze mezi celočíselnými typy a typy s plovoucí řádovou čárkou | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions3.c |
| 37 | numeric_conversions3.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/numeric_conversions3.ll |
| 38 | switch1.c | konstrukce rozvětvení naprogramovaná v jazyku C, první varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch1.c |
| 39 | switch1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch1.ll |
| 40 | switch2.c | konstrukce rozvětvení naprogramovaná v jazyku C, druhá varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch2.c |
| 41 | switch2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch2.ll |
| 42 | switch3.c | konstrukce rozvětvení naprogramovaná v jazyku C, třetí varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch3.c |
| 43 | switch3.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch3.ll |
| 44 | switch4.c | konstrukce rozvětvení naprogramovaná v jazyku C, čtvrtá varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch4.c |
| 45 | switch4.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch4.ll |
| 46 | switch5.c | konstrukce rozvětvení naprogramovaná v jazyku C, pátá varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch5.c |
| 47 | switch5.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch5.ll |
| 48 | switch6.c | konstrukce rozvětvení naprogramovaná v jazyku C, šestá varianta | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch6.c |
| 49 | switch6.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/switch6.ll |
| 50 | arith_operators_float.c | aritmetické operátory programovacího jazyka C | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators_float.c |
| 51 | arith_operators_float.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators_float.ll |
| 52 | comparison_operators_float.c | operátory pro porovnání programovacího jazyka C | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators_float.c |
| 53 | comparison_operators_float.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators_float.ll |
| 54 | goto.c | funkce obsahující konstrukci goto | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/goto.c |
| 55 | goto.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/goto.ll |
| 56 | select.c | rozvětvení na základě podmínky, které vede k vygenerování instrukce select | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/select.c |
| 57 | select.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/select.ll |
| 58 | stack.c | volání několika funkcí, použití nebo nepoužití zásobníku při volání | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/stack.c |
| 59 | stack.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/stack.ll |
| 60 | array_get_int.c | přečtení prvku z pole | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_get_int.c |
| 61 | array_get_int.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_get_int.ll |
| 62 | array_set_int1.c | zápis prvků do pole, řešení používající indexy | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_set_int1.c |
| 63 | array_set_int1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_set_int1.ll |
| 64 | array_set_int2.c | zápis prvků do pole, řešení používající ukazatele | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_set_int2.c |
| 65 | array_set_int2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_set_int2.ll |
| 66 | array_sum_int1.c | součet prvků v poli (přeloženo bez použití vektorových instrukcí) | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_sum_int1.c |
| 67 | array_sum_int1.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_sum_int1.ll |
| 68 | array_sum_int2.c | součet prvků v poli (přeloženo bez použití vektorových instrukcí) | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_sum_int2.c |
| 69 | array_sum_int2.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/array_sum_int2.ll |
| 70 | bitwise_operators.c | bitové operace | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/bitwise_operators.c |
| 71 | bitwise_operators.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/bitwise_operators.ll |
| 72 | remainder.c | výpočet zbytku po dělení celých čísel | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/remainder.c |
| 73 | remainder.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/remainder.ll |
| 74 | arith_operators_fp16.c | základní aritmetické operace s hodnotami typu half float | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators_fp16.c |
| 75 | arith_operators_fp16.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/arith_operators_fp16.ll |
| 76 | comparison_operators_fp16.c | porovnání dvojice hodnot typu half float | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators_fp16.c |
| 77 | comparison_operators_fp16.ll | výsledek překladu céčkovského zdrojového kódu do mezijazyka | https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/comparison_operators_fp16.ll |
20. Odkazy na Internetu
- Is intermediate representation (such as bytecodes or .net IL) still an advantage?
https://stackoverflow.com/questions/35061333/is-intermediate-representation-such-as-bytecodes-or-net-il-still-an-advantage - Intermediate Representation vs Byte Code
https://cs.stackexchange.com/questions/163398/intermediate-representation-vs-byte-code - Getting the intermediate representation in gcc
https://forum.osdev.org/viewtopic.php?t=22845&sid=11f6e77d24bda7fcc2c9ef6a5be4e6b2 - Intermediate Representations
https://www.cs.cornell.edu/courses/cs4120/2023sp/notes/ir/ - Why do we use intermediate representations / languages?
https://mortoray.com/why-we-use-intermediate-representations/ - Unwrapping intermediate representations
https://mortoray.com/unwrapping-intermediate-representations/ - Understanding Python Code Flow From Source to Execution
https://medium.com/@azan96593/understanding-python-code-flow-from-source-to-execution-ebeea870ef83 - Why most compilers use AST, instead generate IR directly?
https://stackoverflow.com/questions/60870622/why-most-compilers-use-ast-instead-generate-ir-directly#60902159 - A Gentle Introduction to LLVM IR
https://mcyoung.xyz/2023/08/01/llvm-ir/ - Why does the compiler need the intermediate representations for link time optimization?
https://stackoverflow.com/questions/75586563/why-does-the-compiler-need-the-intermediate-representations-for-link-time-optimi - pyrefact na PyPi
https://pypi.org/project/pyrefact/ - Repositář projektu pyrefact
https://github.com/OlleLindgren/pyrefact - pyrefact jako plugin do VSCode
https://marketplace.visualstudio.com/items?itemName=olleln.pyrefact - pyrefact-vscode-extension (repositář)
https://github.com/OlleLindgren/pyrefact-vscode-extension - Best Python Refactoring Tools for 2023
https://www.developer.com/languages/python/best-python-refactoring-tools/ - Python Refactoring: Techniques, Tools, and Best Practices
https://www.codesee.io/learning-center/python-refactoring - Lexikální a syntaktická analýza zdrojových kódů programovacího jazyka Python
https://www.root.cz/clanky/lexikalni-a-syntakticka-analyza-zdrojovych-kodu-programovaciho-jazyka-python/ - Lexikální a syntaktická analýza zdrojových kódů programovacího jazyka Python (2.část)
https://www.root.cz/clanky/lexikalni-a-syntakticka-analyza-zdrojovych-kodu-programovaciho-jazyka-python-2-cast/ - Lexikální a syntaktická analýza zdrojových kódů programovacího jazyka Python (3.část)
https://www.root.cz/clanky/lexikalni-a-syntakticka-analyza-zdrojovych-kodu-programovaciho-jazyka-python-3-cast/ - Lexikální a syntaktická analýza zdrojových kódů jazyka Python (4.část)
https://www.root.cz/clanky/lexikalni-a-syntakticka-analyza-zdrojovych-kodu-jazyka-python-4-cast/ - Knihovna LibCST umožňující snadnou modifikaci zdrojových kódů Pythonu
https://www.root.cz/clanky/knihovna-libcst-umoznujici-snadnou-modifikaci-zdrojovych-kodu-pythonu/ - LibCST – dokumentace
https://libcst.readthedocs.io/en/latest/index.html - libCST na PyPi
https://pypi.org/project/libcst/ - libCST na GitHubu
https://github.com/Instagram/LibCST - Inside The Python Virtual Machine
https://leanpub.com/insidethepythonvirtualmachine - module-py_compile
https://docs.python.org/3.8/library/py_compile.html - Given a python .pyc file, is there a tool that let me view the bytecode?
https://stackoverflow.com/questions/11141387/given-a-python-pyc-file-is-there-a-tool-that-let-me-view-the-bytecode - The structure of .pyc files
https://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html - Python Bytecode: Fun With Dis
http://akaptur.github.io/blog/2013/08/14/python-bytecode-fun-with-dis/ - Python's Innards: Hello, ceval.c!
http://tech.blog.aknin.name/category/my-projects/pythons-innards/ - Golang Compilation and Execution
https://golangtutorial.com/golang-compilation-and-execution/ - Mezijazyk (Wikipedie)
https://cs.wikipedia.org/wiki/Mezijazyk - The LLVM Compiler Infrastructure
https://www.llvm.org/ - GCC internals
https://gcc.gnu.org/onlinedocs/gccint/index.html - GCC Developer Options
https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html - What is Gimple?
https://mschiralli1.wordpress.com/2024/12/01/what-is-gimple/ - The Conceptual Structure of GCC
https://www.cse.iitb.ac.in/grc/intdocs/gcc-conceptual-structure.html - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - Abstract syntax tree
https://en.wikipedia.org/wiki/Abstract_syntax_tree - Lexical analysis
https://en.wikipedia.org/wiki/Lexical_analysis - Parser
https://en.wikipedia.org/wiki/Parsing#Parser - Parse tree
https://en.wikipedia.org/wiki/Parse_tree - Derivační strom
https://cs.wikipedia.org/wiki/Deriva%C4%8Dn%C3%AD_strom - Python doc: ast — Abstract Syntax Trees
https://docs.python.org/3/library/ast.html - Python doc: tokenize — Tokenizer for Python source
https://docs.python.org/3/library/tokenize.html - SymbolTable
https://docs.python.org/3.8/library/symtable.html - 5 Amazing Python AST Module Examples
https://www.pythonpool.com/python-ast/ - Intro to Python ast Module
https://medium.com/@wshanshan/intro-to-python-ast-module-bbd22cd505f7 - Golang AST Package
https://golangdocs.com/golang-ast-package - AP8, IN8 Regulární jazyky
http://statnice.dqd.cz/home:inf:ap8 - AP9, IN9 Konečné automaty
http://statnice.dqd.cz/home:inf:ap9 - AP10, IN10 Bezkontextové jazyky
http://statnice.dqd.cz/home:inf:ap10 - AP11, IN11 Zásobníkové automaty, Syntaktická analýza
http://statnice.dqd.cz/home:inf:ap11 - Introduction to YACC
https://www.geeksforgeeks.org/introduction-to-yacc/ - Introduction of Lexical Analysis
https://www.geeksforgeeks.org/introduction-of-lexical-analysis/?ref=lbp - Write your own filter
http://pygments.org/docs/filterdevelopment/ - Write your own lexer
http://pygments.org/docs/lexerdevelopment/ - Write your own formatter
http://pygments.org/docs/formatterdevelopment/ - Compiler Construction/Lexical analysis
https://en.wikibooks.org/wiki/Compiler_Construction/Lexical_analysis - Compiler Design – Lexical Analysis
https://www.tutorialspoint.com/compiler_design/compiler_design_lexical_analysis.htm - Lexical Analysis – An Intro
https://www.scribd.com/document/383765692/Lexical-Analysis - Python AST Visualizer
https://github.com/pombredanne/python-ast-visualizer - What is an Abstract Syntax Tree
https://blog.bitsrc.io/what-is-an-abstract-syntax-tree-7502b71bde27 - Why is AST so important
https://medium.com/@obernardovieira/why-is-ast-so-important-b1e7d6c29260 - Emily Morehouse-Valcarcel – The AST and Me – PyCon 2018
https://www.youtube.com/watch?v=XhWvz4dK4ng - Python AST Parsing and Custom Linting
https://www.youtube.com/watch?v=OjPT15y2EpE - Chase Stevens – Exploring the Python AST Ecosystem
https://www.youtube.com/watch?v=Yq3wTWkoaYY - Full Grammar specification
https://docs.python.org/3/reference/grammar.html - Playing with GCC’s GIMPLE: How to Generate, Save, and Modify Intermediate Code (Tutorial + Examples)
https://www.tutorialpedia.org/blog/playing-with-gcc-s-intermediate-gimple-format/ - A Deep Dive Into LLVM IR
https://medium.com/@abdulraheembeigh/a-deep-dive-into-llvm-ir-b5aa81beb474 - Nulová operace
https://cs.wikipedia.org/wiki/Nulov%C3%A1_operace - NOP (code)
https://en.wikipedia.org/wiki/NOP_(code) - LLVM Bitcode File Format
https://llvm.org/docs/BitCodeFormat.html - LLVM IR Language Reference
https://deepwiki.com/llvm-mirror/llvm/2.1-llvm-ir-language-reference - Ackermann function
https://en.wikipedia.org/wiki/Ackermann_function - Primitive recursive function
https://en.wikipedia.org/wiki/Primitive_recursive_function