Technologie mezijazyků (mezikódů) a bajtkódů: LLVM IR (2)

Dnes
Doba čtení: 47 minut

Sdílet

Dva kolegové pracují na svých dvou počítačích
Autor: Shutterstock
Popíšeme si další instrukce LLVM IR, tedy instrukce generované překladačem Clang. Bude se jednat o instrukce používané při výpočtech s FP hodnotami, dále konverzi dat, ale například i překladech rozvětvení.

Obsah

1. Technologie mezijazyků (mezikódů) a bajtkódů v moderních interpretrech a překladačích: LLVM IR (2)

2. Aritmetické operace a porovnání hodnot typu floatdouble

3. Operace po porovnání hodnot typu floatdouble

4. Konverze mezi hodnotami různých datových typů

5. Třetí demonstrační příklad: konverze mezi celočíselnými typy bez znaménka

6. Čtvrtý demonstrační příklad: konverze mezi celočíselnými hodnotami se znaménkem

7. Instrukce používané při konverzích celočíselných hodnot se znaménkem i bez znaménka

8. Pátý demonstrační příklad: konverze mezi hodnotami s plovoucí řádovou čárkou a celočíselnými hodnotami

9. Instrukce používané při sofistikovanějších konverzích

10. Základní operace s ukazateli

11. Kód provádějící rozvětvení

12. Překlad jednoduché programové smyčky s podmínkou (while)

13. Překlad počítané programové smyčky (for)

14. Rozvětvení konstrukcí switch-case

15. Další varianta rozvětvení konstrukcí switch-case

16. Nové instrukce používané při větvení

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

20. Odkazy na Internetu

1. Technologie mezijazyků (mezikódů) a bajtkódů v moderních interpretrech a překladačích: LLVM IR (2)

V dnešním článku budeme pokračovat v tématu, kterému jsme se začali věnovat již v článku předchozím. Popíšeme si další instrukce LLVM IR, tedy například instrukce generované překladačem Clang atd. Pochopitelně ovšem nemá smysl si jednotlivé instrukce pouze vypsat, protože je nutné znát i kontext – tj. ve kterých případech je překladače generují. Z tohoto důvodu si vždy ukážeme zdrojový kód naprogramovaný v jazyku C (většinou je generovaný s pomocí maker) a k němu odpovídající mezikód reprezentovaný v LLVM IR. Seznam všech instrukcí LLVM IR, s nimiž se tímto způsobem seznámíme, je uveden v tabulce vypsané v sedmnácté kapitole.

Poznámka: pro překlad z jazyka C do LLVM IR s využitím Clangu je použit přepínač -Os povolující optimalizace na velikost výsledného kódu. Bez tohoto přepínače totiž vznikají „sáhodlouhé“ mezikódy s mnoha lokálními proměnnými atd., mezi nimiž by se instrukce, které budou popsány, ztrácely.

2. Aritmetické operace a porovnání hodnot typu floatdouble

V dnešním prvním demonstračním příkladu si ověříme, jakým způsobem se přeloží základní aritmetické operace prováděné s hodnotami s plovoucí řádovou čárkou (floating point), tj. konkrétně s hodnotami typu float a double. Uvidíme, že se v LLVM IR použijí poněkud jiné instrukce, než tomu bylo v případě celočíselných datových typů. V céčkovském zdrojovém kódu opět použijeme makra:

#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(float)
ALL(double)
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/a­rith_operators_float.c.

Po zpracování preprocesorem programovacího jazyka C získáme následující céčkovský kód:

float neg_float(float x) {return -x;}
float add_float(float x, float y) {return x+y;}
float sub_float(float x, float y) {return x-y;}
float mul_float(float x, float y) {return x*y;}
float div_float(float x, float y) {return x/y;}
 
double neg_double(double x) {return -x;}
double add_double(double x, double y) {return x+y;}
double sub_double(double x, double y) {return x-y;}
double mul_double(double x, double y) {return x*y;}
double div_double(double x, double y) {return x/y;}

A po překladu výše uvedeného kódu do LLVM IR je již patrné, jaké instrukce se v tomto případě volají:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @neg_float(float noundef %0) local_unnamed_addr #0 {
  %2 = fneg float %0
  ret float %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @add_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fadd float %0, %1
  ret float %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @sub_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fsub float %0, %1
  ret float %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @mul_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fmul float %0, %1
  ret float %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @div_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fdiv float %0, %1
  ret float %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @neg_double(double noundef %0) local_unnamed_addr #0 {
  %2 = fneg double %0
  ret double %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @add_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fadd double %0, %1
  ret double %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @sub_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fsub double %0, %1
  ret double %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @mul_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fmul double %0, %1
  ret double %3
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @div_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fdiv double %0, %1
  ret double %3
}

Všechny nové instrukce (navíc včetně již minule popsané instrukce fadd) si pochopitelně opět můžeme vypsat ve formě tabulky:

# 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

Povšimněte si, že instrukce určené pro zpracování hodnot typu double se od instrukcí s typem float odlišují pouze uvedením modifikátoru se jménem typu.

3. Operace po porovnání hodnot typu floatdouble

V předchozím článku jsme si popsali i instrukce určené pro porovnání celočíselných hodnot. Připomeňme si, že se ve skutečnosti jednalo o jedinou instrukci nazvanou icmp (integer compare) s variantami určenými dalším modifikátorem eq, ne, slt atd. Zbývá nám tedy zjistit, jakým způsobem dochází k porovnání hodnot typu float a/nebo double. Zdrojový kód z minulého článku upravíme do podoby, která bude pracovat právě s numerickými hodnotami s plovoucí řádovou čárkou:

#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(float)
ALL(double)
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/com­parison_operators_float.c.

Preprocesor programovacího jazyka C z výše uvedeného zdrojového kódu vytvoří následující kód:

int eq_float(float x, float y) {return x==y;}
int ne_float(float x, float y) {return x!=y;}
int lt_float(float x, float y) {return x<y;}
int le_float(float x, float y) {return x<=y;}
int gt_float(float x, float y) {return x>y;}
int ge_float(float x, float y) {return x>=y;}
 
int eq_double(double x, double y) {return x==y;}
int ne_double(double x, double y) {return x!=y;}
int lt_double(double x, double y) {return x<y;}
int le_double(double x, double y) {return x<=y;}
int gt_double(double x, double y) {return x>y;}
int ge_double(double x, double y) {return x>=y;}

Výsledek překladu tohoto zdrojového kódu do LLVM IR dopadne následovně:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 2) i32 @eq_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp oeq float %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_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp une float %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_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp olt float %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_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp ole float %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_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp ogt float %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_float(float noundef %0, float noundef %1) local_unnamed_addr #0 {
  %3 = fcmp oge float %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 @eq_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp oeq double %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_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp une double %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_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp olt double %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_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp ole double %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_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp ogt double %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_double(double noundef %0, double noundef %1) local_unnamed_addr #0 {
  %3 = fcmp oge double %0, %1
  %4 = zext i1 %3 to i32
  ret i32 %4
}

Z tohoto výpisu opět můžeme snadno zjistit, které operace se pro porovnávání používají:

# Jméno instrukce Parametry Stručný popis instrukce
1 fcmp oeq float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „rovno“
2 fcmp une float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „nerovno“
3 fcmp olt float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší než“
4 fcmp ole float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší nebo rovno“
5 fcmp ogt float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší než“
6 fcmp oge float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší nebo rovno“
Poznámka: první znak „o“ v modifikátoru znamená ordered zatímco „u“ znamená unordered.
. Tyto varianty se od sebe odlišují v tom, jakým způsobem se pracuje se speciální hodnotou Not a Number.

4. Konverze mezi hodnotami různých datových typů

Ve zdrojových kódech se velmi často setkáme s konverzemi, při nichž se hodnota určitého typu konvertuje na hodnotu typu jiného. V některých programovacích jazycích mohou být tyto konverze (resp. některé konverze) provedeny implicitně, ovšem například v jazyce Go se musí všechny konverze zapisovat explicitně. Nezávisle na použitém programovacím jazyku je však nutné, aby LLVM IR konverze podporoval, což znamená, že musí obsahovat příslušné instrukce. Většinu těchto instrukcí si ukážeme v následující trojici demonstračních příkladů, které konverze rozdělují na tři skupiny:

  1. Konverze mezi celočíselnými typy bez znaménka (unsigned)
  2. Konverze mezi celočíselnými typy se znaménkem (signed)
  3. Konverze mezi typy s plovoucí řádovou čárkou (float, double)
  4. Mix konverzí: převod reálného čísla na celé číslo atd.

5. Třetí demonstrační příklad: konverze mezi celočíselnými typy bez znaménka

V dnešním třetím demonstračním příkladu je (opět na základě maker) vygenerováno několik céčkovských funkcí, které provádí všechny kombinace konverzí celočíselných datových typů bez znaménka. Pro úplnost se vytváří i funkce provádějící konverzi hodnoty na stejný typ, což ovšem vede k velmi jednoduchému LLVM IR. Původní zdrojový kód tohoto příkladu vypadá následovně:

#include <stdint.h>
 
#define CONVERT(type1, type2) type2 convert_##type1##_to_##type2(type1 x) {return (type2)x;}
 
CONVERT(uint8_t, uint8_t)
CONVERT(uint8_t, uint16_t)
CONVERT(uint8_t, uint32_t)
CONVERT(uint8_t, uint64_t)
 
CONVERT(uint16_t, uint8_t)
CONVERT(uint16_t, uint16_t)
CONVERT(uint16_t, uint32_t)
CONVERT(uint16_t, uint64_t)
 
CONVERT(uint32_t, uint8_t)
CONVERT(uint32_t, uint16_t)
CONVERT(uint32_t, uint32_t)
CONVERT(uint32_t, uint64_t)
 
CONVERT(uint64_t, uint8_t)
CONVERT(uint64_t, uint16_t)
CONVERT(uint64_t, uint32_t)
CONVERT(uint64_t, uint64_t)
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/nu­meric_conversions1.c.

Výsledek preprocessingu výše uvedeného zdrojového kódu s makry:

uint8_t  convert_uint8_t_to_uint8_t(uint8_t x) {return (uint8_t)x;}
uint16_t convert_uint8_t_to_uint16_t(uint8_t x) {return (uint16_t)x;}
uint32_t convert_uint8_t_to_uint32_t(uint8_t x) {return (uint32_t)x;}
uint64_t convert_uint8_t_to_uint64_t(uint8_t x) {return (uint64_t)x;}
 
uint8_t  convert_uint16_t_to_uint8_t(uint16_t x) {return (uint8_t)x;}
uint16_t convert_uint16_t_to_uint16_t(uint16_t x) {return (uint16_t)x;}
uint32_t convert_uint16_t_to_uint32_t(uint16_t x) {return (uint32_t)x;}
uint64_t convert_uint16_t_to_uint64_t(uint16_t x) {return (uint64_t)x;}
 
uint8_t  convert_uint32_t_to_uint8_t(uint32_t x) {return (uint8_t)x;}
uint16_t convert_uint32_t_to_uint16_t(uint32_t x) {return (uint16_t)x;}
uint32_t convert_uint32_t_to_uint32_t(uint32_t x) {return (uint32_t)x;}
uint64_t convert_uint32_t_to_uint64_t(uint32_t x) {return (uint64_t)x;}
 
uint8_t  convert_uint64_t_to_uint8_t(uint64_t x) {return (uint8_t)x;}
uint16_t convert_uint64_t_to_uint16_t(uint64_t x) {return (uint16_t)x;}
uint32_t convert_uint64_t_to_uint32_t(uint64_t x) {return (uint32_t)x;}
uint64_t convert_uint64_t_to_uint64_t(uint64_t x) {return (uint64_t)x;}

LLVM IR, který vznikne překladem s využitím Clangu, by měl vypadat následovně:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i8 @convert_uint8_t_to_uint8_t(i8 noundef returned zeroext %0) local_unnamed_addr #0 {
  ret i8 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local zeroext range(i16 0, 256) i16 @convert_uint8_t_to_uint16_t(i8 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = zext i8 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 256) i32 @convert_uint8_t_to_uint32_t(i8 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = zext i8 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 0, 256) i64 @convert_uint8_t_to_uint64_t(i8 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = zext i8 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i8 @convert_uint16_t_to_uint8_t(i16 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = trunc i16 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i16 @convert_uint16_t_to_uint16_t(i16 noundef returned zeroext %0) local_unnamed_addr #0 {
  ret i16 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 0, 65536) i32 @convert_uint16_t_to_uint32_t(i16 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = zext i16 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 0, 65536) i64 @convert_uint16_t_to_uint64_t(i16 noundef zeroext %0) local_unnamed_addr #0 {
  %2 = zext i16 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i8 @convert_uint32_t_to_uint8_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i32 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i16 @convert_uint32_t_to_uint16_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i32 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @convert_uint32_t_to_uint32_t(i32 noundef returned %0) local_unnamed_addr #0 {
  ret i32 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 0, 4294967296) i64 @convert_uint32_t_to_uint64_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = zext i32 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i8 @convert_uint64_t_to_uint8_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i16 @convert_uint64_t_to_uint16_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @convert_uint64_t_to_uint32_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i64 @convert_uint64_t_to_uint64_t(i64 noundef returned %0) local_unnamed_addr #0 {
  ret i64 %0
}

6. Čtvrtý demonstrační příklad: konverze mezi celočíselnými hodnotami se znaménkem

V dnešním čtvrtém demonstračním příkladu provedeme stejné operace konverze, jako v příkladu předchozím, ovšem nyní s celočíselnými hodnotami se znaménkem (signed). To nám umožní zjistit, zda se budou instrukce LLVM IR lišit či nikoli:

#include <stdint.h>
 
#define CONVERT(type1, type2) type2 convert_##type1##_to_##type2(type1 x) {return (type2)x;}
 
CONVERT(int8_t, int8_t)
CONVERT(int8_t, int16_t)
CONVERT(int8_t, int32_t)
CONVERT(int8_t, int64_t)
 
CONVERT(int16_t, int8_t)
CONVERT(int16_t, int16_t)
CONVERT(int16_t, int32_t)
CONVERT(int16_t, int64_t)
 
CONVERT(int32_t, int8_t)
CONVERT(int32_t, int16_t)
CONVERT(int32_t, int32_t)
CONVERT(int32_t, int64_t)
 
CONVERT(int64_t, int8_t)
CONVERT(int64_t, int16_t)
CONVERT(int64_t, int32_t)
CONVERT(int64_t, int64_t)
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/nu­meric_conversions2.c.

Zdrojový kód po zpracování preprocesorem jazyka C:

int8_t  convert_int8_t_to_int8_t(int8_t x) {return (int8_t)x;}
int16_t convert_int8_t_to_int16_t(int8_t x) {return (int16_t)x;}
int32_t convert_int8_t_to_int32_t(int8_t x) {return (int32_t)x;}
int64_t convert_int8_t_to_int64_t(int8_t x) {return (int64_t)x;}
 
int8_t  convert_int16_t_to_int8_t(int16_t x) {return (int8_t)x;}
int16_t convert_int16_t_to_int16_t(int16_t x) {return (int16_t)x;}
int32_t convert_int16_t_to_int32_t(int16_t x) {return (int32_t)x;}
int64_t convert_int16_t_to_int64_t(int16_t x) {return (int64_t)x;}
 
int8_t  convert_int32_t_to_int8_t(int32_t x) {return (int8_t)x;}
int16_t convert_int32_t_to_int16_t(int32_t x) {return (int16_t)x;}
int32_t convert_int32_t_to_int32_t(int32_t x) {return (int32_t)x;}
int64_t convert_int32_t_to_int64_t(int32_t x) {return (int64_t)x;}
 
int8_t  convert_int64_t_to_int8_t(int64_t x) {return (int8_t)x;}
int16_t convert_int64_t_to_int16_t(int64_t x) {return (int16_t)x;}
int32_t convert_int64_t_to_int32_t(int64_t x) {return (int32_t)x;}
int64_t convert_int64_t_to_int64_t(int64_t x) {return (int64_t)x;}

Výsledek překladu do LLVM IR bude vypadat následovně:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i8 @convert_int8_t_to_int8_t(i8 noundef returned signext %0) local_unnamed_addr #0 {
  ret i8 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local signext range(i16 -128, 128) i16 @convert_int8_t_to_int16_t(i8 noundef signext %0) local_unnamed_addr #0 {
  %2 = sext i8 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 -128, 128) i32 @convert_int8_t_to_int32_t(i8 noundef signext %0) local_unnamed_addr #0 {
  %2 = sext i8 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 -128, 128) i64 @convert_int8_t_to_int64_t(i8 noundef signext %0) local_unnamed_addr #0 {
  %2 = sext i8 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i8 @convert_int16_t_to_int8_t(i16 noundef signext %0) local_unnamed_addr #0 {
  %2 = trunc i16 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i16 @convert_int16_t_to_int16_t(i16 noundef returned signext %0) local_unnamed_addr #0 {
  ret i16 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i32 -32768, 32768) i32 @convert_int16_t_to_int32_t(i16 noundef signext %0) local_unnamed_addr #0 {
  %2 = sext i16 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 -32768, 32768) i64 @convert_int16_t_to_int64_t(i16 noundef signext %0) local_unnamed_addr #0 {
  %2 = sext i16 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i8 @convert_int32_t_to_int8_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i32 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i16 @convert_int32_t_to_int16_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i32 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @convert_int32_t_to_int32_t(i32 noundef returned %0) local_unnamed_addr #0 {
  ret i32 %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local range(i64 -2147483648, 2147483648) i64 @convert_int32_t_to_int64_t(i32 noundef %0) local_unnamed_addr #0 {
  %2 = sext i32 %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i8 @convert_int64_t_to_int8_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i8
  ret i8 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef signext i16 @convert_int64_t_to_int16_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i16
  ret i16 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @convert_int64_t_to_int32_t(i64 noundef %0) local_unnamed_addr #0 {
  %2 = trunc i64 %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i64 @convert_int64_t_to_int64_t(i64 noundef returned %0) local_unnamed_addr #0 {
  ret i64 %0
}

7. Instrukce používané při konverzích celočíselných hodnot se znaménkem i bez znaménka

V tento okamžik již můžeme sepsat všechny konverzní instrukce, které dokážou konvertovat celočíselnou hodnotu se znaménkem nebo bez znaménka na jinou celočíselnou hodnotu s větším nebo menším počtem bitů. Rozšíření počtu bitů je realizováno instrukcemi se jmény zext a sext, zatímco o snížení počtu bitů se stará jediná instrukce nazvaná trunc. Pokud má být provedeno rozšíření bez znaménka (doplnění nejvyšších bitů nulami), využívá se instrukce zext (zero extension), pokud se naopak má provést znaménkové rozšíření (kopie původního znaménka do nejvyšších bitů), využívá se instrukce sext (sign extension):

# Jméno instrukce Parametry Stručný popis instrukce
1 zext typ1, operand, typ2 bezznaménkové rozšíření z typu1 na typ2
2 sext typ1, operand, typ2 znaménkové rozšíření z typu1 na typ2
2 trunc typ1, operand, typ2 zmenšení bitové šířky z typu1 na typ2

8. Pátý demonstrační příklad: konverze mezi hodnotami s plovoucí řádovou čárkou a celočíselnými hodnotami

Ještě se musíme seznámit s konverzemi, které umožňují převádět hodnoty s plovoucí řádovou čárkou (float, double) na celočíselné hodnoty a naopak. Tyto konverze si ověříme díky následujícímu demonstračnímu příkladu, jehož zdrojový kód vypadá následovně:

#include <stdint.h>
 
#define CONVERT(type1, type2) type2 convert_##type1##_to_##type2(type1 x) {return (type2)x;}
 
CONVERT(float, float)
CONVERT(float, double)
CONVERT(double, float)
CONVERT(double, double)
 
CONVERT(int32_t, float)
CONVERT(int32_t, double)
CONVERT(int64_t, float)
CONVERT(int64_t, double)
 
CONVERT(float, int32_t)
CONVERT(double, int32_t)
CONVERT(float, int64_t)
CONVERT(double, int64_t)
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/nu­meric_conversions3.c.

Výsledek po zpracování tohoto zdrojového kódu preprocesorem jazyka C:

float  convert_float_to_float(float x) {return (float)x;}
double convert_float_to_double(float x) {return (double)x;}
float  convert_double_to_float(double x) {return (float)x;}
double convert_double_to_double(double x) {return (double)x;}
 
float  convert_int32_t_to_float(int32_t x) {return (float)x;}
double convert_int32_t_to_double(int32_t x) {return (double)x;}
float  convert_int64_t_to_float(int64_t x) {return (float)x;}
double convert_int64_t_to_double(int64_t x) {return (double)x;}
 
int32_t convert_float_to_int32_t(float x) {return (int32_t)x;}
int32_t convert_double_to_int32_t(double x) {return (int32_t)x;}
int64_t convert_float_to_int64_t(float x) {return (int64_t)x;}
int64_t convert_double_to_int64_t(double x) {return (int64_t)x;}

Výsledný mezikód v LLVM IR vypadá takto:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @convert_float_to_float(float noundef returned %0) local_unnamed_addr #0 {
  ret float %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @convert_float_to_double(float noundef %0) local_unnamed_addr #0 {
  %2 = fpext float %0 to double
  ret double %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @convert_double_to_float(double noundef %0) local_unnamed_addr #0 {
  %2 = fptrunc double %0 to float
  ret float %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @convert_double_to_double(double noundef returned %0) local_unnamed_addr #0 {
  ret double %0
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @convert_int32_t_to_float(i32 noundef %0) local_unnamed_addr #0 {
  %2 = sitofp i32 %0 to float
  ret float %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @convert_int32_t_to_double(i32 noundef %0) local_unnamed_addr #0 {
  %2 = sitofp i32 %0 to double
  ret double %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef float @convert_int64_t_to_float(i64 noundef %0) local_unnamed_addr #0 {
  %2 = sitofp i64 %0 to float
  ret float %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef double @convert_int64_t_to_double(i64 noundef %0) local_unnamed_addr #0 {
  %2 = sitofp i64 %0 to double
  ret double %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local i32 @convert_float_to_int32_t(float noundef %0) local_unnamed_addr #0 {
  %2 = fptosi float %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local i32 @convert_double_to_int32_t(double noundef %0) local_unnamed_addr #0 {
  %2 = fptosi double %0 to i32
  ret i32 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local i64 @convert_float_to_int64_t(float noundef %0) local_unnamed_addr #0 {
  %2 = fptosi float %0 to i64
  ret i64 %2
}
 
; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local i64 @convert_double_to_int64_t(double noundef %0) local_unnamed_addr #0 {
  %2 = fptosi double %0 to i64
  ret i64 %2
}

9. Instrukce používané při sofistikovanějších konverzích

Opět si můžeme vypsat konverzní instrukce použité v předchozím mezikódu. Navíc byly do tabulky dopsány i instrukce určené pro konverzi z/do celočíselného typu bez znaménka (unsigned), které sice v demonstračním příkladu použity nebyly, ale doplňují celou sadu dostupných instrukcí:

# Jméno instrukce Parametry Stručný popis instrukce
1 fpext typ1, hodnota, typ2 převod FP hodnoty z typu 1 na typ 2 (rozšíření přesnosti/rozsahu)
2 fptrunc typ1, hodnota, typ2 převod FP hodnoty z typu 1 na typ 2 (zmenšení přesnosti/rozsahu)
3 sitofp typ1, hodnota, typ2 převod celočíselné hodnoty se znaménkem na FP hodnotu
4 uitofp typ1, hodnota, typ2 převod celočíselné hodnoty bez znaménka na FP hodnotu
4 fptosi typ1, hodnota, typ2 převod FP hodnoty na celočíselnou hodnotu se znaménkem (signed)
5 fptoui typ1, hodnota, typ2 převod FP hodnoty na celočíselnou hodnotu bez znaménka (unsigned)

10. Základní operace s ukazateli

V této kapitole si ukážeme, jakým způsobem se přeloží céčkovský kód pracující s ukazateli do mezikódu LLVM IR. Začneme velmi jednoduchou funkcí, která dokáže prohodit hodnoty dvou parametrů předaných referencí (ukazatelem). Nejjednodušší podoba této funkce může vypadat takto:

void swap(int *a, int *b) {
    int c = *a;
    *a = *b;
    *b = c;
}
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/po­inters1.c.

V mezikódu můžeme vidět, že se používají instrukce load a store, které již dobře známe. Překladač Clang navíc kód do jisté míry upravil tak, že se používají dvě pomocné proměnné %3 a %4, což vede k určité symetrii zápisu:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(argmem: readwrite) uwtable
define dso_local void @swap(ptr nocapture noundef %0, ptr nocapture noundef %1) local_unnamed_addr #0 {
  %3 = load i32, ptr %0, align 4, !tbaa !3
  %4 = load i32, ptr %1, align 4, !tbaa !3
  store i32 %4, ptr %0, align 4, !tbaa !3
  store i32 %3, ptr %1, align 4, !tbaa !3
  ret void
}

Zkusme nyní práci překladači ztížit, a to takovým způsobem, že do funkce swap budeme předávat dva netypové ukazatele. Přetypování bude provedeno až přímo v těle funkce (toto v žádném případě není pěkný kód, ovšem v praxi se můžeme s netypovými ukazateli relativně často setkat, takže s nimi musí jak Clang, tak i (možná) LLVM IR počítat):

void swap(void *a, void *b) {
    int c = *(int*)a;
    *(int*)a = *(int*)b;
    *(int*)b = c;
}
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/po­inters2.c.

Zajímavé je, že překlad v tomto případě proběhne naprosto stejným způsobem – mezikód bude obsahovat totožné instrukce:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(argmem: readwrite) uwtable
define dso_local void @swap(ptr nocapture noundef %0, ptr nocapture noundef %1) local_unnamed_addr #0 {
  %3 = load i32, ptr %0, align 4, !tbaa !3
  %4 = load i32, ptr %1, align 4, !tbaa !3
  store i32 %4, ptr %0, align 4, !tbaa !3
  store i32 %3, ptr %1, align 4, !tbaa !3
  ret void
}

11. Kód provádějící rozvětvení

Další důležitou součástí LLVM IR jsou instrukce provádějící rozvětvení. Tyto instrukce jsou použity i při překladu programových smyček, rekurze atd. Podívejme se tedy na způsob překladu následujícího jednoduchého příkladu, ve kterém je rozvětvení použito ve výrazu s ternárním operátorem:

unsigned int divide(unsigned int u, unsigned int v) {
    return v==0 ? -1 : u/v;
}
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/bran­ching1.c.

V mezikódu, který vznikl překladem do LLVM IR, můžeme vidět dvojici nových instrukcí br a phi:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @divide(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
  %3 = icmp eq i32 %1, 0
  br i1 %3, label %6, label %4
 
4:                                                ; preds = %2
  %5 = udiv i32 %0, %1
  br label %6
 
6:                                                ; preds = %2, %4
  %7 = phi i32 [ %5, %4 ], [ -1, %2 ]
  ret i32 %7
}

12. Překlad jednoduché programové smyčky s podmínkou (while)

Podobným způsobem si můžeme přeložit i funkci s programovou smyčkou, konkrétně s programovou smyčkou obsahující podmínku. V tomto konkrétním případě se jedná o výpočet největšího společného dělitele:

unsigned int gcd(unsigned int u, unsigned int v) {
    while (v) {
        int t = u;
        u = v;
        v = t % v;
    }
    return u;
}
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/bran­ching2.c.

Výsledek překladu této funkce do LLVM IR bude vypadat následovně:

; Function Attrs: nofree norecurse nosync nounwind optsize memory(none) uwtable
define dso_local i32 @gcd(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
  %3 = icmp eq i32 %1, 0
  br i1 %3, label %9, label %4
 
4:                                                ; preds = %2, %4
  %5 = phi i32 [ %6, %4 ], [ %0, %2 ]
  %6 = phi i32 [ %7, %4 ], [ %1, %2 ]
  %7 = urem i32 %5, %6
  %8 = icmp eq i32 %7, 0
  br i1 %8, label %9, label %4, !llvm.loop !3
 
9:                                                ; preds = %4, %2
  %10 = phi i32 [ %0, %2 ], [ %6, %4 ]
  ret i32 %10
}

Opět zde můžeme vidět dvojici instrukcí pojmenovaných br a phi.

13. Překlad počítané programové smyčky (for)

Kromě programových smyček s podmínkou testovanou na začátku nebo na konci každé iterace se v praxi pochopitelně setkáme i se smyčkami, v nichž se používá počitadlo počtu opakování popř. složitější podmínka založená na hodnotách jedné či více proměnných. Takový typ smyčky lze v jazyce C realizovat s využitím klíčového slova for, například takto:

long fibonacciIter(int n) {
    if (n <= 1) {
        return n;
    }
    long result = 0;
    long n1 = 0;
    long n2 = 1;
    for(n--; n > 0; n--) {
        result = n1 + n2;
        n1 = n2;
        n2 = result;
    }
    return result;
}
Poznámka: zdrojový kód tohoto demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/fibonacci.c.

Ve vygenerovaném mezikódu je opět patrné, že se používají instrukce br a phi:

; Function Attrs: nofree norecurse nosync nounwind optsize memory(none) uwtable
define dso_local i64 @fibonacciIter(i32 noundef %0) local_unnamed_addr #0 {
  %2 = icmp slt i32 %0, 2
  br i1 %2, label %3, label %5
 
3:                                                ; preds = %1
  %4 = sext i32 %0 to i64
  br label %12
 
5:                                                ; preds = %1, %5
  %6 = phi i64 [ %10, %5 ], [ 1, %1 ]
  %7 = phi i64 [ %6, %5 ], [ 0, %1 ]
  %8 = phi i32 [ %9, %5 ], [ %0, %1 ]
  %9 = add nsw i32 %8, -1
  %10 = add nsw i64 %6, %7
  %11 = icmp samesign ugt i32 %8, 2
  br i1 %11, label %5, label %12, !llvm.loop !3
 
12:                                               ; preds = %5, %3
  %13 = phi i64 [ %4, %3 ], [ %10, %5 ]
  ret i64 %13
}
Poznámka: žádné nové instrukce v tomto již poměrně netriviálním kódu nenalezneme.

14. Rozvětvení konstrukcí switch-case

S instrukcemi LLVM IR provádějícími rozvětvení se setkáme i při překladu programové konstrukce typu switch-case. Ukažme si tuto konstrukci na netriviálním demonstračním příkladu, který překladač nedokáže převést na výpočet s využitím vyhledávací tabulky, takže bude donucen realizovat skutečné rozvětvení:

unsigned int numeric_value(unsigned int x, unsigned int y) {
    switch (x) {
        case 0:
            y++;
            break;
        case 1:
            y+=2;
            break;
        case 2:
            y-=2;
            break;
        case 3:
            y--;
            break;
    }
    return y;
}

Výsledkem překladu bude mezikód, který je dosti zajímavý, protože obsahuje tabulku skoků switch. Tato tabulka obsahuje testované hodnoty a odpovídající cíle skoků:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @numeric_value(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
  switch i32 %0, label %11 [
    i32 0, label %3
    i32 1, label %5
    i32 2, label %7
    i32 3, label %9
  ]
 
3:                                                ; preds = %2
  %4 = add i32 %1, 1
  br label %11
 
5:                                                ; preds = %2
  %6 = add i32 %1, 2
  br label %11
 
7:                                                ; preds = %2
  %8 = add i32 %1, -2
  br label %11
 
9:                                                ; preds = %2
  %10 = add i32 %1, -1
  br label %11
 
11:                                               ; preds = %2, %9, %7, %5, %3
  %12 = phi i32 [ %1, %2 ], [ %10, %9 ], [ %8, %7 ], [ %6, %5 ], [ %4, %3 ]
  ret i32 %12
}
Poznámka: opět zde můžeme vidět i instrukce br a phi, zde použité při výskoku z větví.

15. Další varianta rozvětvení konstrukcí switch-case

V dnešním posledním demonstračním příkladu bude opět použita programová konstrukce typu switch-case, ovšem nyní nebudou testované hodnoty tvořit číselnou řadu 0, 1, 2, 3. Namísto toho použijeme (pro překladač náhodné) hodnoty:

unsigned int numeric_value(unsigned int x, unsigned int y) {
    switch (x) {
        case 42:
            y++;
            break;
        case 100:
            y+=2;
            break;
        case 6502:
            y-=2;
            break;
        case 8080:
            y--;
            break;
    }
    return y;
}

Překlad této konstrukce do mezikódu LLVM IR dopadne takto:

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef i32 @numeric_value(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {
  switch i32 %0, label %11 [
    i32 42, label %3
    i32 100, label %5
    i32 6502, label %7
    i32 8080, label %9
  ]
 
3:                                                ; preds = %2
  %4 = add i32 %1, 1
  br label %11
 
5:                                                ; preds = %2
  %6 = add i32 %1, 2
  br label %11
 
7:                                                ; preds = %2
  %8 = add i32 %1, -2
  br label %11
 
9:                                                ; preds = %2
  %10 = add i32 %1, -1
  br label %11
 
11:                                               ; preds = %2, %9, %7, %5, %3
  %12 = phi i32 [ %1, %2 ], [ %10, %9 ], [ %8, %7 ], [ %6, %5 ], [ %4, %3 ]
  ret i32 %12
}

16. Nové instrukce používané při větvení

V předchozích výpisech LLVM IR jsme mohli vidět následující pětici instrukcí určených pro řízení větvení, vícenásobného rozvětvení, programových smyček atd.:

# Jméno instrukce Parametry Stručný popis instrukce
1 br label číslo nepodmíněný skok na nastavené návěští
2 br podmínka label číslo podmíněný skok na nastavené návěští
3 ret hodnota návrat z funkce
4 switch tabulka rozeskok na základě tabulky s dvojicemi hodnota:návěští
5 phi typ [hodnota, hodnota] … instrukce použitá při konverzi mezi LLVM IR a SSA

První dvě instrukce br provádí nepodmíněný nebo podmíněný skok, a to prakticky stejným způsobem, jako je tomu v assembleru (za případnou podmínkou vždy následuje cíl skoku). Třetí instrukce ret realizuje výskok z funkce (tuto instrukci již dobře známe). Čtvrtá instrukce switch slouží pro realizaci rozeskoku na základě tabulky s hodnotami a cíli skoku. A konečně pátá instrukce phi je svou sémantikou nejsložitější. Používá se při konverzi z LLVM IR (mezikódu) do SSA (Static single-assignment), což je v oblasti překladačů důležitá problematika, které se budeme věnovat v samostatném článku:

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
5 ret hodnota návrat z funkce
       
6 add hodnota1, hodnota2 součet dvou celočíselných hodnot
7 sub hodnota1, hodnota2 rozdíl dvou celočíselných hodnot
8 mul hodnota1, hodnota2 součin dvou celočíselných hodnot
9 sdiv hodnota1, hodnota2 podíl dvou celočíselných hodnot, které mají znaménko (signed)
10 udiv hodnota1, hodnota2 podíl dvou celočíselných hodnot, které jsou bezznaménkové (unsigned)
       
11 shl hodnota, posun aritmetický či bitový posun doleva
12 ashr hodnota, posun aritmetický posun doprava
13 lshr hodnota, posun bitový posun doprava
       
14 icmp eq hodnota1, hodnota2 porovnání dvou operandů na relaci „rovno“
15 icmp ne hodnota1, hodnota2 porovnání dvou operandů na relaci „nerovno“
16 icmp slt hodnota1, hodnota2 porovnání dvou operandů se znaménkem na relaci „menší než“
17 icmp sle hodnota1, hodnota2 porovnání dvou operandů se znaménkem na relaci „menší nebo rovno“
18 icmp sgt hodnota1, hodnota2 porovnání dvou operandů se znaménkem na relaci „větší než“
19 icmp sge hodnota1, hodnota2 porovnání dvou operandů se znaménkem na relaci „větší nebo rovno“
20 icmp ult hodnota1, hodnota2 porovnání dvou operandů bez znaménka na relaci „menší než“
21 icmp ule hodnota1, hodnota2 porovnání dvou operandů bez znaménka na relaci „menší nebo rovno“
22 icmp ugt hodnota1, hodnota2 porovnání dvou operandů bez znaménka na relaci „větší než“
23 icmp uge hodnota1, hodnota2 porovnání dvou operandů bez znaménka na relaci „větší nebo rovno“
       
24 fneg float/double změna znaménka hodnoty s plovoucí řádovou čárkou
25 fadd hodnota1, hodnota2 součet dvou hodnot s plovoucí řádovou čárkou
26 fsub hodnota1, hodnota2 rozdíl dvou hodnot s plovoucí řádovou čárkou
27 fmul hodnota1, hodnota2 součin dvou hodnot s plovoucí řádovou čárkou
28 fdiv hodnota1, hodnota2 podíl dvou hodnot s plovoucí řádovou čárkou
       
29 fcmp oeq float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „rovno“
30 fcmp une float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „nerovno“
31 fcmp olt float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší než“
32 fcmp ole float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „menší nebo rovno“
33 fcmp ogt float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší než“
34 fcmp oge float/double, float/double porovnání dvou operandů s plovoucí řádovou čárkou na relaci „větší nebo rovno“
       
35 zext typ1, operand, typ2 bezznaménkové rozšíření z typu1 na typ2
36 sext typ1, operand, typ2 znaménkové rozšíření z typu1 na typ2
37 trunc typ1, operand, typ2 zmenšení bitové šířky z typu1 na typ2
       
38 fpext typ1, hodnota, typ2 převod FP hodnoty z typu 1 na typ 2 (rozšíření přesnosti/rozsahu)
39 fptrunc typ1, hodnota, typ2 převod FP hodnoty z typu 1 na typ 2 (zmenšení přesnosti/rozsahu)
40 sitofp typ1, hodnota, typ2 převod celočíselné hodnoty se znaménkem na FP hodnotu
41 uitofp typ1, hodnota, typ2 převod celočíselné hodnoty bez znaménka na FP hodnotu
41 fptosi typ1, hodnota, typ2 převod FP hodnoty na celočíselnou hodnotu se znaménkem (signed)
42 fptoui typ1, hodnota, typ2 převod FP hodnoty na celočíselnou hodnotu bez znaménka (unsigned)
       
43 br label číslo nepodmíněný skok na nastavené návěští
44 br podmínka label číslo podmíněný skok na nastavené návěští
45 switch tabulka rozeskok na základě tabulky s dvojicemi hodnota:návěští
46 phi typ [hodnota, hodnota] … instrukce použitá při konverzi mezi LLVM IR a SSA

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 článku předchozím, 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 \
           comparison_operators.ll comparison_operators_float.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
 
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 $@

Pro překlad všech demonstračních příkladů postačuje zadat příkaz:

Školení Hacking

$ 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ím i 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/no­op_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/no­op_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/ad­d_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/ad­d_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/ad­d_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/a­rith_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/a­rith_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/nu­meric_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/nu­meric_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/com­parison_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/com­parison_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/po­inters1.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/po­inters2.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/bran­ching1.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/bran­ching1.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/bran­ching2.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/bran­ching2.ll
       
26 add_arrays1.c součet prvků polí, předání polí odkazem https://github.com/tisnik/8bit-fame/blob/master/LLVM_IR/ad­d_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/ad­d_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/ad­d_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/ad­d_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/nu­meric_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/nu­meric_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/nu­meric_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/nu­meric_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/nu­meric_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/nu­meric_conversions3.ll
       
38 switch1.c 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 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 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 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 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 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/a­rith_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/a­rith_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/com­parison_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/com­parison_operators_float.ll

20. Odkazy na Internetu

  1. Is intermediate representation (such as bytecodes or .net IL) still an advantage?
    https://stackoverflow.com/qu­estions/35061333/is-intermediate-representation-such-as-bytecodes-or-net-il-still-an-advantage
  2. Intermediate Representation vs Byte Code
    https://cs.stackexchange.com/qu­estions/163398/intermedia­te-representation-vs-byte-code
  3. Getting the intermediate representation in gcc
    https://forum.osdev.org/vi­ewtopic.php?t=22845&sid=11f6e77­d24bda7fcc2c9ef6a5be4e6b2
  4. Intermediate Representations
    https://www.cs.cornell.edu/cou­rses/cs4120/2023sp/notes/ir/
  5. Why do we use intermediate representations / languages?
    https://mortoray.com/why-we-use-intermediate-representations/
  6. Unwrapping intermediate representations
    https://mortoray.com/unwrapping-intermediate-representations/
  7. Understanding Python Code Flow From Source to Execution
    https://medium.com/@azan96593/un­derstanding-python-code-flow-from-source-to-execution-ebeea870ef83
  8. Why most compilers use AST, instead generate IR directly?
    https://stackoverflow.com/qu­estions/60870622/why-most-compilers-use-ast-instead-generate-ir-directly#60902159
  9. A Gentle Introduction to LLVM IR
    https://mcyoung.xyz/2023/08/01/llvm-ir/
  10. Why does the compiler need the intermediate representations for link time optimization?
    https://stackoverflow.com/qu­estions/75586563/why-does-the-compiler-need-the-intermediate-representations-for-link-time-optimi
  11. pyrefact na PyPi
    https://pypi.org/project/pyrefact/
  12. Repositář projektu pyrefact
    https://github.com/OlleLin­dgren/pyrefact
  13. pyrefact jako plugin do VSCode
    https://marketplace.visual­studio.com/items?itemName=o­lleln.pyrefact
  14. pyrefact-vscode-extension (repositář)
    https://github.com/OlleLin­dgren/pyrefact-vscode-extension
  15. Best Python Refactoring Tools for 2023
    https://www.developer.com/lan­guages/python/best-python-refactoring-tools/
  16. Python Refactoring: Techniques, Tools, and Best Practices
    https://www.codesee.io/learning-center/python-refactoring
  17. 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/
  18. 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/
  19. 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/
  20. 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/
  21. 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/
  22. LibCST – dokumentace
    https://libcst.readthedoc­s.io/en/latest/index.html
  23. libCST na PyPi
    https://pypi.org/project/libcst/
  24. libCST na GitHubu
    https://github.com/Instagram/LibCST
  25. Inside The Python Virtual Machine
    https://leanpub.com/insidet­hepythonvirtualmachine
  26. module-py_compile
    https://docs.python.org/3­.8/library/py_compile.html
  27. Given a python .pyc file, is there a tool that let me view the bytecode?
    https://stackoverflow.com/qu­estions/11141387/given-a-python-pyc-file-is-there-a-tool-that-let-me-view-the-bytecode
  28. The structure of .pyc files
    https://nedbatchelder.com/blog/200804/the_str­ucture_of_pyc_files.html
  29. Python Bytecode: Fun With Dis
    http://akaptur.github.io/blog/2013/08/14/pyt­hon-bytecode-fun-with-dis/
  30. Python's Innards: Hello, ceval.c!
    http://tech.blog.aknin.na­me/category/my-projects/pythons-innards/
  31. Golang Compilation and Execution
    https://golangtutorial.com/golang-compilation-and-execution/
  32. Mezijazyk (Wikipedie)
    https://cs.wikipedia.org/wi­ki/Mezijazyk
  33. The LLVM Compiler Infrastructure
    https://www.llvm.org/
  34. GCC internals
    https://gcc.gnu.org/online­docs/gccint/index.html
  35. GCC Developer Options
    https://gcc.gnu.org/online­docs/gcc/Developer-Options.html
  36. What is Gimple?
    https://mschiralli1.wordpres­s.com/2024/12/01/what-is-gimple/
  37. The Conceptual Structure of GCC
    https://www.cse.iitb.ac.in/grc/in­tdocs/gcc-conceptual-structure.html
  38. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  39. ASM Home page
    http://asm.ow2.org/
  40. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  41. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  42. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  43. BCEL Home page
    http://commons.apache.org/bcel/
  44. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  45. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  46. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  47. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  48. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  49. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  50. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  51. Javassist
    http://www.jboss.org/javassist/
  52. Byteman
    http://www.jboss.org/byteman
  53. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  54. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  55. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  56. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  57. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  58. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  59. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  60. Abstract syntax tree
    https://en.wikipedia.org/wi­ki/Abstract_syntax_tree
  61. Lexical analysis
    https://en.wikipedia.org/wi­ki/Lexical_analysis
  62. Parser
    https://en.wikipedia.org/wi­ki/Parsing#Parser
  63. Parse tree
    https://en.wikipedia.org/wi­ki/Parse_tree
  64. Derivační strom
    https://cs.wikipedia.org/wi­ki/Deriva%C4%8Dn%C3%AD_strom
  65. Python doc: ast — Abstract Syntax Trees
    https://docs.python.org/3/li­brary/ast.html
  66. Python doc: tokenize — Tokenizer for Python source
    https://docs.python.org/3/li­brary/tokenize.html
  67. SymbolTable
    https://docs.python.org/3­.8/library/symtable.html
  68. 5 Amazing Python AST Module Examples
    https://www.pythonpool.com/python-ast/
  69. Intro to Python ast Module
    https://medium.com/@wshanshan/intro-to-python-ast-module-bbd22cd505f7
  70. Golang AST Package
    https://golangdocs.com/golang-ast-package
  71. AP8, IN8 Regulární jazyky
    http://statnice.dqd.cz/home:inf:ap8
  72. AP9, IN9 Konečné automaty
    http://statnice.dqd.cz/home:inf:ap9
  73. AP10, IN10 Bezkontextové jazyky
    http://statnice.dqd.cz/home:inf:ap10
  74. AP11, IN11 Zásobníkové automaty, Syntaktická analýza
    http://statnice.dqd.cz/home:inf:ap11
  75. Introduction to YACC
    https://www.geeksforgeeks­.org/introduction-to-yacc/
  76. Introduction of Lexical Analysis
    https://www.geeksforgeeks­.org/introduction-of-lexical-analysis/?ref=lbp
  77. Write your own filter
    http://pygments.org/docs/fil­terdevelopment/
  78. Write your own lexer
    http://pygments.org/docs/le­xerdevelopment/
  79. Write your own formatter
    http://pygments.org/docs/for­matterdevelopment/
  80. Compiler Construction/Lexical analysis
    https://en.wikibooks.org/wi­ki/Compiler_Construction/Le­xical_analysis
  81. Compiler Design – Lexical Analysis
    https://www.tutorialspoin­t.com/compiler_design/com­piler_design_lexical_analy­sis.htm
  82. Lexical Analysis – An Intro
    https://www.scribd.com/do­cument/383765692/Lexical-Analysis
  83. Python AST Visualizer
    https://github.com/pombredanne/python-ast-visualizer
  84. What is an Abstract Syntax Tree
    https://blog.bitsrc.io/what-is-an-abstract-syntax-tree-7502b71bde27
  85. Why is AST so important
    https://medium.com/@obernar­dovieira/why-is-ast-so-important-b1e7d6c29260
  86. Emily Morehouse-Valcarcel – The AST and Me – PyCon 2018
    https://www.youtube.com/wat­ch?v=XhWvz4dK4ng
  87. Python AST Parsing and Custom Linting
    https://www.youtube.com/wat­ch?v=OjPT15y2EpE
  88. Chase Stevens – Exploring the Python AST Ecosystem
    https://www.youtube.com/wat­ch?v=Yq3wTWkoaYY
  89. Full Grammar specification
    https://docs.python.org/3/re­ference/grammar.html
  90. 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/
  91. A Deep Dive Into LLVM IR
    https://medium.com/@abdulraheembeigh/a-deep-dive-into-llvm-ir-b5aa81beb474
  92. Nulová operace
    https://cs.wikipedia.org/wi­ki/Nulov%C3%A1_operace
  93. NOP (code)
    https://en.wikipedia.org/wi­ki/NOP_(code)
  94. LLVM Bitcode File Format
    https://llvm.org/docs/Bit­CodeFormat.html
  95. LLVM IR Language Reference
    https://deepwiki.com/llvm-mirror/llvm/2.1-llvm-ir-language-reference

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.