Lidi dělají divné věci. Například spouští Linux v Excelu. Využít je emulátor RISC-V mini-rv32ima sestavený jako knihovna DLL, která je volaná z makra VBA (Visual Basic for Applications).
Revolut nabídne neomezený mobilní tarif za 12,50 eur (312 Kč). Aktuálně startuje ve Velké Británii a Německu.
Společnost Amazon miliardáře Jeffa Bezose vypustila na oběžnou dráhu první várku družic svého projektu Kuiper, který má z vesmíru poskytovat vysokorychlostní internetové připojení po celém světě a snažit se konkurovat nyní dominantnímu Starlinku nejbohatšího muže planety Elona Muska.
Poslední aktualizací začal model GPT-4o uživatelům příliš podlézat. OpenAI jej tak vrátila k předchozí verzi.
Google Chrome 136 byl prohlášen za stabilní. Nejnovější stabilní verze 136.0.7103.59 přináší řadu novinek z hlediska uživatelů i vývojářů. Podrobný přehled v poznámkách k vydání. Opraveno bylo 8 bezpečnostních chyb. Vylepšeny byly také nástroje pro vývojáře.
Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Tento blog byl smazán autorem
Tiskni
Sdílej:
Proč? www.gnu.org/software/lightning/ Je to jednoduché, portabilní, velmi dobře zdokumentované, a jako bonus to není C++ :)
using namespace AsmJit; X86 a; // mov eax, dword ptr[ebx + 16] a.mov(eax, dword_ptr(ebx, 16)); // mov dword_ptr [eax + ebx * 4 + 16], edx a.mov(dword_ptr(eax, ebx, TIMES_4, 16), edx); // mov ah, al a.mov(ah, al); // mov al, byte ptr[ecx] a.mov(al, byte_ptr(ecx)); // add dword_ptr [esi], eax a.add(dword_ptr(esi), eax); // addps xmm1, dqword ptr [eax] a.addps(xmm1, dqword_ptr(eax));Pokud lighting používáte, tak můžete odpovědět přepsáním těch instrukcí pro lighting, uvidíte, že těch C maker pro jednotlivé instrukce je potřeba mnohem víc. Druhá věc je podpora pro odhalování chyb. AsmJit vám některé chyby odhalí už při kompilaci a runtime chyby je možné odhalit v debug módu (a nekontrolovat je při release). Třeba následující kód vám AsmJit nezkompiluje:
// esi není sse registr a.addps(esi, dqword_ptr(eax));Další zápor oproti C a makrům je pro mě i licence. AsmJit můžete díky MIT vložit do kteréhokoliv projektu a linkovat staticky.
Všechno je to 1:1, s výjimkou toho intelího SIB, to se skutečně musí napsat jako 3 instrukce, a protože to nemá peephole, tak z toho i 3 instrukce vylezou. IMHO malá cena za to že nebudete vázán na konkrétní procesor. Ta EA se 3 termy a ještě se scale se ale používá minimálně, a obyčejný base + index nebo base + immediate adresování Lightning samozřejmě pro i386 kompiluje jako jedinou instrukci.
// mov dword_ptr [eax + ebx * 4 + 16], edx lshi(R1, R1, 2); // ebx *= 4 addr(R0, R0, R1); // eax += ebx stxi(R0, 16, R2);
Nojo, jenže když bude uživatel pracovat s konkrétním procesorem, pak je celá knihovna jen jakýmsi glorifikovaným wrapperem, jehož smysl je hlavně v tom že místo:
memcpy(out, "\x89\x4C\x98\x10", 4); out += 4;
můžete psát
asm.mov(dword_ptr(eax, ebx, TIMES_4, 16), ecx);
To první je jednodušší, výrazně rychlejší, a abych pochopil co to dělá tak mi stačí disasembler. To druhé vyžaduje studium zdrojáku, tedy dohledání funkcí, hodnot konstant apod, v případě C++ pak ještě prohledávání base tříd a pod. Pravda, jednu "vysokoúrovňovou" funkci to asi mít bude, bude to umět vytvářet a následně resolvovat dopředné odkazy, na to memcpy() nestačí. Netvrdím že je to k ničemu, když člověk tvoří něco co zrovna potřebuje, tak to obvykle opravdu potřebuje, ale přijde mi že je to v podstatě jednoduchá věc zbytečně zabalená do nějakého api. U Lightning ty makra mají smysl, protože pro každý CPU se implementují jinak. Tady si tím už jistý nejsem.
// OK asm.mov(dword_ptr(eax, ebx, TIMES_4, 16), ecx); // Chyba, špatné parametry (díky C++ už při kompilaci .cpp souboru) asm.mov(dword_ptr(eax, 16, ebx, TIMES_4), ecx);Kdybych například ve vašem příkladu udělal chybu, tak se jen vygenerují jiné opcodes a chybu zjistíte až při běhu aplikace (v ideálním případě to spadne, ale nemusí).
// záměrná chyba "\x89\x4C\x10\x98"V dalším zápisku bych to chtěl trošku víc rozebrat i s nějakým lepším příkladem, na kterém by šlo vidět lepší využití.
Tak jsem si to konečně stáhnul, rozbalil, a trochu prohlédl. Je to docela čitelné, jen si myslím že autor zbytečně používá úplně nepotřebné abstrakce. Třeba nechápu proč je Register třída, proč to není jednoduše enum. Nebylo by třeba blbnout s operator==() apod, a bylo by to čitelnější. To že něco existuje v nějaké učebnici C++ ještě neznamená že je to dobré, a že je to třeba používat.
// CPU Registers. // // 1) We would prefer to use an enum, but enum values are assignment- // compatible with int, which has caused code-generation bugs. // // 2) We would prefer to use a class instead of a struct but we don't like // the register initialization to depend on the particular initialization // order (which appears to be different on OS X, Linux, and Windows for the // installed versions of C++ we tried). Using a struct permits C-style // "initialization". Also, the Register objects cannot be const as this // forces initialization stubs in MSVC, making us dependent on initialization // order. // // 3) By not using an enum, we are possibly preventing the compiler from // doing certain constant folds, which may significantly reduce the // code generated for some assembly instructions (because they boil down // to a few constants). If this is a problem, we could change the code // such that we use an enum in optimized mode, and the struct in debug // mode. This way we get the compile-time error checking in debug mode // and best performance in optimized code. // struct Register ...Já jsem to překopal v tom smyslu, že jsem ke každému registru ještě přidal jednoznačné ID, ze kterého jdou vyčíst i jiné informace než číslo registru pro X86. Tímto způsobem je možé psát assert() u některých funkcí, a kontrolovat jestli není v kódu chyba. Některé instrukce, kde se používají MMX nebo SSE registry vám zase nepustí jako parametr GP registr. Napříklád toto:
// OK a.mov(al, ah); a.mov(eax, ebx); // Chyba, u mov nelze použít MMX a SSE2 registry, na to jsou // jiné instrukce (movd, movq, movdq). Ve staré verzi, která // se dá momentálně stáhnout ty MMX ještě jdou, ale nově // jsem to vyhodil do jiné instrukce, přesně jak je to popsané // v Intel manuálu. // (chyba bohužel se projeví až v runtime) a.mov(mm0, mm1); a.mov(xmm0, xmm1);Ale neprojde třeba ani, když si spletete velikost ukazatele:
// OK a.mov(al, byte_ptr(eax)); a.mov(eax, dword_ptr(eax)); // Chyba a.mov(al, dword_ptr(eax)); a.mov(eax, byte_ptr(eax));V budoucnu plánuju přidat těch assertů mnohem víc, teď jsem na to neměl zase tolik času to dovést k dokonalosti
We would prefer to use an enum, but enum values are assignment-compatible with int
Řekl bych, že to není pravda.
$ more t.cc enum foo { a, b, c }; foo bar() { return 100; } void baz() { foo x = 1; } $ gcc -c t.cc t.cc: In function ‘foo bar()’: t.cc:2: error: invalid conversion from ‘int’ to ‘foo’ t.cc: In function ‘void baz()’: t.cc:3: error: invalid conversion from ‘int’ to ‘foo’
Aha, tak int => enum sice řve, ale enum => int bohužel bez jakéhokoliv warningu projde, a to i při -Wall
:/
Vypadá to úžasně.
Jen jakožto uživatel Gentoo nechápu, v čem spočívá výhoda JIT, když stejnou práci může udělat už překladač v době překladu. (A jako bonus se programátor nemusí trápit s assemblerem.)
Zakladni princip JIT je v tom ze clovek z nejakeho duvodu potrebuje distribuovat jednu binarku na mnoho architektur (typicky binarni distribuce) a presto chce aby mu to jelo alespon trochu rychle. Pokud je mozne distribuovat zdrojove kody postrada JIT z velke casti smysl (i kdyz ano i tady se obcas najde nejake vyuziti).
instrukce [x]mm, [x]mm/mem[32|64|128]Kdyby JIT kompilace pro grafiku neměla smysl, tak nevznikají projekty jako orc nebo genrender. Všechno závisí na znalostech a schopnostech programátora. Věřím, že například cairo by mohlo být klidně 4x rychlejší, ale musel by se najít člověk a hlavně čas na to, aby to někdo mohl implementovat. Ještě k tomu jit bych dodal to, že při kompilaci na míru můžeme některé konstanty úplně vyhodit nebo předpočítat operace s konstantama, které jsou nutné k výpočtu. Další optimalizace by mohla být například vkládání prefetch instrukcí nebo používat Non Temporal read / write. jednoduchý JIT by pak mohl vypadat třeba takto:
X86 a; // eax *= i; if (i == 0) { // clear: x = 0; a.xor(eax, eax); } else if (i == 1) { // nop } else if (isPowerOf2(i)) { // shift instead of multiply: x <<= shiftOf(i); a.shl(eax, imm(shiftOf(i)); } else { // multiply: x *= i a.imul(eax, imm(i)); }Tento jednoduchý příklad ukazuje eliminaci jednoho registru, který může být v inner loop cyklu hodně užitečný.
Když jste to nakousl, tak by mě zajímalo, co to ten AsmJit vlastně je? Spíše než jako překladač mi přijde jako optimalizátor, protože vstupem mu je assembler a výstupem opět assembler.
Třeba u výroby funkce složené ze dvou jiných si dokážu přestavit optimalizaci formu minimalizace složeného automatu. Ale takto u explicitního kódu si nedokážu přestavit, jak by taková optimalizace mohla rozumně fungovat. (Něco jiného by bylo, kdyby se JIT-kompilovalo z vysokoúrovňového jazyka (třeba algebraického výrazu) do assembleru).
Snad Tě potěší, že první nahlášený issue obsahuje i patch Jinak neuvažoval jsi nad použitím LLVM? Tím by si pak rovnou podporoval všechny architektury (stálo by pak sice si zkontrolovat, jestli to z IR do X86 zkompilovalo stejně (nebo více) efektivně jako Tebou rukou napsaný kód, ale snad funguje dobře, když je okolo něj takový buzz). Každopádně řádka
X86 a;
je hrozně krásná.
vim ~/.emacs
Dnes jsem vydal nový projekt, na kterém ve volných chvílích pracuju. Jedná se o JIT překladač assembleru pro C++, který chci použít pro generování blit funkcí pro jednu grafickou knihovnu (FOG). Vytvořit JIT assembler, a pochopit celý princip, na kterém je toto generování kódu založené (generování opcodes) není až tak těžké. Intel X86 je bezesporu komplikovaná architektura, ale s použitím knížky Intel Architecture Software Developers Manual to byla spíš zábava.
Celý JIT je souborem několika tříd v C++, které reprezentují jednotlivé části generování kódu. Obsahuje třídy Register, MMRegister a XMMRegister reprezentující registry. Základní třída Register také může reprezentovat menší registry, které sdílí prostor s 8 základními (třeba AL, AH, AX, ...). Každý registr má přiřazené unikátní ID, ze kterého je možné vyčíst kód registru (v X86 architektuře číslo 0-7), typ registru (GPB, GPW, GPD, MMX, SSE) a velikost registru (to vše je zakódované v malém 8 bitovém neznaménkovém čísle).
Další nutnost pro vytváření JIT assembleru je možnost adresovat místa v paměti. X86 architektura obsahuje poměrně velké možnosti, od jednoduché adresace (např [eax]) až po komplikované adresace (např [eax + 4*edx + 16). Pro adresování je použitá třída Op (zkratka pro Operand), která může obsahovat všechny typy adresací, navíc registry a neměnné hodnoty (immediates). Trída Op vlastné zaobaluje všechny operandy, které je možné při generování kódu použít.
Úplně poslední věc, bez které se při generování kódu nedá obejít jsou labely (nebo návěští). Některými opovrhované goto v C je v asm vlastné jediná možnost, jak dělat podmíněné i nepodmíněné skoky. K definici skoku slouží třída Label. Konstruktor třídy zanechá label jako unbound, to znamená, že pozice skoku ještě nebyla definovaná, ale takto nezinicializovaný label už JE možné použít pro skok na kód, jehož pozice zatím není známá. Pomocí bind() se definuje pozice skoku a tato funkce zárověň opraví všechny místa, které na tuto zatím nedefinovanou pozici ukazovaly.
(Tento odstavec asi není úplně srozumitelný)
Poslední třída, kterou bych chtěl představit, se jmenuje X86. Jak už název napovídá, jedná se o hlavní třídu, která se používá pro samotné generování kódu. Instance třídy si uchovává interní buffer, do kterého přidává emitované instrukce v binární podobě (opcodes). Obsahuje instrukční sadu X86 až po SSE (SSE2 a další přidám v budoucnu).
A k čemu je to dobré?
Vyměnil jsem si pár emailů s lidma, kteří se zajímají o počítačovou grafiku jako já. Dodnes se všechny funkce dělali ručně a jednotlivé možnosti CPU se vždycky implementovaly také jen ručně a mnohdy se na nové možnosti CPU vůbec nedostalo (např. X Server používá maximálně MMX). Pomocí JIT překladače je možné generovat kód dynamicky, za běhu přidávat například nový pixel formát a generovat i kód pro takové funkce, které normálně fungují jako složení z více funkcí. Průběh low level grafické funkce vypadá normálně nějak takto - nahraj a zkonvertuj pixely SRC a DST do formátu ARGB32 -> zkombinuj -> zakóduj výsledné pixely zpět do DST. Pomocí JIT překladače je možné některé speciální (neboli nejčastěji používané) operace zkompilovat na míru a vyhneme se tím vlastně tří konverzí (a zvýšíme výkon).
Dodatek: Konverze se samozřejmě nedělá tam, kde pracujeme s nativním formátem (většinou ARGB32 premultiplied), ale například při blitování obrázku, který je jen RGB24 už konverze probíhá (minimálně 1).
A úplně na závěr úplný příklad toho, jak dynamicky vygenerovat a spustit jednoduchý kód. Zkoušel jsem to ve Windows i v Linuxu, takže případní zájemci můžou hned začít experimentovat a hlásit chyby . Zdrojové kódy a tento příklad je možné stáhnout s použitím odkazu na konci zápisku.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "../AsmJit/AsmJitX86.h" #include "../AsmJit/AsmJitVM.h" typedef int (*VoidFn)(); int main(int argc, char* argv[]) { using namespace AsmJit; // Create function by dynamic way X86 a; // Prolog a.push(ebp); a.mov(ebp, esp); // Mov 1024 to EAX, EAX is also return value. a.mov(eax, imm(1024)); // Epilog a.mov(esp, ebp); a.pop(ebp); a.ret(); // Alloc execute enabled memory and call generated function size_t vsize; void *vmem = VM::alloc(a.codeSize(), &vsize, true); memcpy(vmem, a.pData, a.codeSize()); // Cast vmem to VoidFn VoidFn fn = (VoidFn)vmem; // Call it int result = fn(); // Memory should be freed, but use VM::free() to do that. VM::free(vmem, vsize); printf("Result from jit function: %d\n", result); return 0; }
Poznámka: Paměť, ve kterém chceme provést exekuci kódu, je nutné alokovat jinak než pomocí malloc(), v Linuxu se používá mmap() a ve Windows VirtualAlloc(). Na obou systémech je možné i paměť dodatečně označit jako EXECUTE (v linuxu pomocí mprotect), ale tímto jsem se zatím nezdržoval.
Knihovnu jsem umístil zde: http://code.google.com/p/asmjit/ pod MIT licencí.
UPDATE:
Na webu je k dispozici nová verze (0.2), byl aplikován patch pro macos (díky) a instrukční sada je nyní kompletní (včetně SSE4). Nyní se budu soustředit spíš na testování a popřípadě přidání podpory pro 64 bit.