├── src ├── styles │ ├── global.css │ └── custom.css ├── tailwind.css ├── assets │ └── houston.webp ├── env.d.ts ├── content │ └── docs │ │ ├── background │ │ ├── 02-The architecture.mdx │ │ ├── 04 Getting Started.mdx │ │ ├── 05-motivation.mdx │ │ ├── 02-MIPS Architecture.mdx │ │ ├── index.mdx │ │ └── 03 about llvm.mdx │ │ ├── reference │ │ ├── mips isa.md │ │ ├── mips-arch.md │ │ └── llvm-backend.md │ │ ├── section 1 │ │ ├── testing.mdx │ │ ├── index.mdx │ │ ├── 03 setting up.mdx │ │ ├── 01 triple.mdx │ │ └── 02 registers.mdx │ │ ├── guides │ │ └── example.md │ │ ├── index.mdx │ │ └── llvm-cg-ref │ │ └── Selection DAG │ │ └── 01 selection dag.mdx ├── scripts │ ├── snippets-loader.ts │ ├── preprocess-snippets.js │ ├── expressive-code.ts │ └── code-caption.ts ├── components │ ├── CollapsibleAside.astro │ ├── InlineComment.astro │ ├── BlurCard.astro │ ├── Collapsible.astro │ └── CodeSnippet.astro ├── content.config.ts └── util │ └── read-snippet.ts ├── run_tests ├── tools └── snippet-parser │ ├── .lit_test_times.txt │ ├── build │ ├── .lit_test_times.txt │ └── Output │ │ └── simple.cpp.script │ ├── tests │ ├── lit-test │ │ ├── .lit_test_times.txt │ │ └── Output │ │ │ └── simple.cpp.script │ ├── same-line-func.cpp │ ├── class.cpp │ ├── nested-blocks.cpp │ ├── snippet-test.cpp │ ├── incorrect_snips.cpp │ ├── templates.cpp │ ├── lit.cfg.py │ └── func-formats.cpp │ ├── test.py │ └── README.md ├── docs ├── MIPS Vol II-A.pdf ├── MIPS Quick Reference.pdf ├── pagefind │ ├── wasm.en.pagefind │ ├── pagefind-entry.json │ ├── wasm.unknown.pagefind │ ├── index │ │ └── en_2cd657f.pf_index │ ├── fragment │ │ ├── en_44b956f.pf_fragment │ │ ├── en_4b71b52.pf_fragment │ │ ├── en_4be7dd4.pf_fragment │ │ ├── en_63c72fe.pf_fragment │ │ ├── en_65a75f1.pf_fragment │ │ ├── en_76f9ec0.pf_fragment │ │ ├── en_9b5a972.pf_fragment │ │ ├── en_9fdebfa.pf_fragment │ │ ├── en_b12a0a8.pf_fragment │ │ ├── en_c06494e.pf_fragment │ │ ├── en_c4d412e.pf_fragment │ │ ├── en_c944465.pf_fragment │ │ ├── en_d02421f.pf_fragment │ │ ├── en_e17d3c6.pf_fragment │ │ └── en_fc33262.pf_fragment │ ├── pagefind.en_c2ffaf6792.pf_meta │ ├── pagefind-modular-ui.css │ ├── pagefind-ui.css │ └── pagefind-modular-ui.js ├── MD00082-2B-MIPS32INT-AFP-06.01.pdf ├── sitemap-index.xml ├── favicon.svg ├── _astro │ ├── MobileTableOfContents.astro_astro_type_script_index_0_lang.C181hMzK.js │ ├── TableOfContents.astro_astro_type_script_index_0_lang.CKWWgpjV.js │ ├── page.7qqag-5g.js │ ├── ec.8zarh.js │ ├── Search.astro_astro_type_script_index_0_lang.Bp05R14F.js │ └── print.BJ0teN4y.css ├── sitemap-0.xml └── 404.html ├── public ├── MIPS Vol II-A.pdf ├── MIPS Quick Reference.pdf ├── MD00082-2B-MIPS32INT-AFP-06.01.pdf └── favicon.svg ├── .env.example ├── for_loop.cpp ├── mips-code ├── hello.asm ├── test_qemu.s ├── run.sh └── mips1.asm ├── tsconfig.json ├── .gitignore ├── package.json ├── example.txt ├── ec.config.mjs ├── astro.config.mjs └── README.md /src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; -------------------------------------------------------------------------------- /run_tests: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | lit -sv tools/snippet-parser/tests -------------------------------------------------------------------------------- /tools/snippet-parser/.lit_test_times.txt: -------------------------------------------------------------------------------- 1 | -3.921676e-02 simple.cpp 2 | -------------------------------------------------------------------------------- /tools/snippet-parser/build/.lit_test_times.txt: -------------------------------------------------------------------------------- 1 | -7.020950e-03 simple.cpp 2 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/lit-test/.lit_test_times.txt: -------------------------------------------------------------------------------- 1 | -3.080773e-02 simple.cpp 2 | -------------------------------------------------------------------------------- /src/tailwind.css: -------------------------------------------------------------------------------- 1 | /* @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; */ 4 | -------------------------------------------------------------------------------- /docs/MIPS Vol II-A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/MIPS Vol II-A.pdf -------------------------------------------------------------------------------- /tools/snippet-parser/test.py: -------------------------------------------------------------------------------- 1 | from lit.main import main 2 | 3 | if __name__ == "__main__": 4 | main() -------------------------------------------------------------------------------- /public/MIPS Vol II-A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/public/MIPS Vol II-A.pdf -------------------------------------------------------------------------------- /src/assets/houston.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/src/assets/houston.webp -------------------------------------------------------------------------------- /docs/MIPS Quick Reference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/MIPS Quick Reference.pdf -------------------------------------------------------------------------------- /docs/pagefind/wasm.en.pagefind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/wasm.en.pagefind -------------------------------------------------------------------------------- /docs/pagefind/pagefind-entry.json: -------------------------------------------------------------------------------- 1 | {"version":"1.3.0","languages":{"en":{"hash":"en_c2ffaf6792","wasm":"en","page_count":15}}} -------------------------------------------------------------------------------- /public/MIPS Quick Reference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/public/MIPS Quick Reference.pdf -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | LLVM_ROOT_DIR="/home/username/llvm-project" 2 | FILECHECK_PATH="/home/username/llvm-project/build/bin/FileCheck" 3 | -------------------------------------------------------------------------------- /docs/pagefind/wasm.unknown.pagefind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/wasm.unknown.pagefind -------------------------------------------------------------------------------- /for_loop.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | 3 | for (int i = 0; i < 10; i++) { 4 | std::cout << i << std::endl; 5 | } 6 | return 0; 7 | } -------------------------------------------------------------------------------- /docs/MD00082-2B-MIPS32INT-AFP-06.01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/MD00082-2B-MIPS32INT-AFP-06.01.pdf -------------------------------------------------------------------------------- /docs/pagefind/index/en_2cd657f.pf_index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/index/en_2cd657f.pf_index -------------------------------------------------------------------------------- /public/MD00082-2B-MIPS32INT-AFP-06.01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/public/MD00082-2B-MIPS32INT-AFP-06.01.pdf -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_44b956f.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_44b956f.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_4b71b52.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_4b71b52.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_4be7dd4.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_4be7dd4.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_63c72fe.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_63c72fe.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_65a75f1.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_65a75f1.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_76f9ec0.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_76f9ec0.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_9b5a972.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_9b5a972.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_9fdebfa.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_9fdebfa.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_b12a0a8.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_b12a0a8.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_c06494e.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_c06494e.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_c4d412e.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_c4d412e.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_c944465.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_c944465.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_d02421f.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_d02421f.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_e17d3c6.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_e17d3c6.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/fragment/en_fc33262.pf_fragment: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/fragment/en_fc33262.pf_fragment -------------------------------------------------------------------------------- /docs/pagefind/pagefind.en_c2ffaf6792.pf_meta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/optimisan/llvm-mips-backend/HEAD/docs/pagefind/pagefind.en_c2ffaf6792.pf_meta -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly LLVM_ROOT_DIR: string; 3 | } 4 | 5 | interface ImportMeta { 6 | readonly env: ImportMetaEnv; 7 | } 8 | -------------------------------------------------------------------------------- /mips-code/hello.asm: -------------------------------------------------------------------------------- 1 | .data 2 | hello: .asciiz "Hello, World!\n" 3 | 4 | .text 5 | main: 6 | li $v0, 4 7 | la $a0, hello 8 | syscall 9 | 10 | li $v0, 10 11 | syscall 12 | -------------------------------------------------------------------------------- /docs/sitemap-index.xml: -------------------------------------------------------------------------------- 1 | https://optimisan.github.io/llvm-mips-backend/sitemap-0.xml -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mips-code/test_qemu.s: -------------------------------------------------------------------------------- 1 | .data 2 | message: .asciiz "Hello, World!\n" 3 | 4 | .text 5 | .globl __start 6 | 7 | __start: 8 | ; li $v0, 4001 9 | ; li $a0, 1 10 | ; la $a1, message 11 | ; li $a2, 13 12 | ; syscall 13 | jr $31 14 | nop 15 | li $v0, 10 # exit 16 | syscall 17 | -------------------------------------------------------------------------------- /src/content/docs/background/02-The architecture.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Architecture 3 | description: Overview of the architecture we are going to follow. 4 | sidebar: 5 | order: 2 6 | --- 7 | 8 | The name of our target is Nova. 9 | 10 | Since this tutorial only implements return and `add` instructions, there is no need 11 | to learn Mips. -------------------------------------------------------------------------------- /src/content/docs/reference/mips isa.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MIPS 32 Architecture 3 | draft: true 4 | --- 5 | 6 | Read the MIPS Architecture for Programmers Volume II-A: The MIPS32 Instruction Set at [MIPS32 Architecture](https://web.archive.org/web/20190616150529/https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00086-2B-MIPS32BIS-AFP-6.06.pdf). 7 | -------------------------------------------------------------------------------- /src/content/docs/section 1/testing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The testing part 3 | description: testing 4 | draft: true 5 | --- 6 | 7 | import CodeSnippet from "../../../components/CodeSnippet.astro"; 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mips-code/run.sh: -------------------------------------------------------------------------------- 1 | pushd mips-code 2 | llvm-mc --arch=mips test_qemu.asm -filetype=obj -o build/test.o 3 | mips-linux-gnu-ld -static build/test.o -o build/test 4 | qemu-mips-static build/test 5 | popd 6 | 7 | 8 | exit 9 | mips-linux-gnu-gcc hello-mips.o -o hello-mips -static 10 | mips-linux-gnu-as hello-mips-llc.s -o hello-mips-llc.o 11 | mips-linux-gnu-gcc hello-mips-llc.o -o hello-mips-llc -static 12 | qemu-mips-static hello-mips-llc -------------------------------------------------------------------------------- /tools/snippet-parser/tests/same-line-func.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser -d %s | %filecheck %s 2 | 3 | #include 4 | 5 | bool isRequired() { return true; } 6 | 7 | int main() { 8 | // CHECK-LABEL: Context[7] 9 | // CHECK-NEXT: [:7] int main 10 | if (isRequired()) { 11 | std::cout << "Required" << std::endl; 12 | void inlineFunc(); 13 | // CHECK-LABEL: Context[11] 14 | // CHECK-NEXT: [:7] int main 15 | } 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/content/docs/guides/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Example Guide 3 | description: A guide in my new Starlight docs site. 4 | --- 5 | 6 | Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. 7 | Writing a good guide requires thinking about what your users are trying to do. 8 | 9 | ## Further reading 10 | 11 | - Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | # build output 3 | dist/ 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | 17 | # environment variables 18 | .env 19 | .env.production 20 | 21 | # macOS-specific files 22 | .DS_Store 23 | 24 | # Folders that I use for local experimentation 25 | me_lua/ 26 | *.log 27 | # build/ includes all folders named 28 | # "build" anywhere in the project 29 | build/ 30 | public/google* -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llvm-luavm-backend", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/starlight": "^0.31.1", 14 | "@expressive-code/plugin-collapsible-sections": "^0.40.2", 15 | "@tailwindcss/vite": "^4.0.14", 16 | "astro": "^5.1.5", 17 | "sharp": "^0.32.5", 18 | "tailwindcss": "^4.0.14" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/lit-test/Output/simple.cpp.script: -------------------------------------------------------------------------------- 1 | set -o pipefail;set -x;{ { set +x; } 2>/dev/null && echo 'RUN: at line 1': 'python3 src/snippet-parser/main.py /home/mirasma/Projects/llvm-mips-backend/src/snippet-parser/tests/simple.cpp | FileCheck /home/mirasma/Projects/llvm-mips-backend/src/snippet-parser/tests/simple.cpp' >&2 && { set -x; } 2>/dev/null && { python3 src/snippet-parser/main.py /home/mirasma/Projects/llvm-mips-backend/src/snippet-parser/tests/simple.cpp | FileCheck /home/mirasma/Projects/llvm-mips-backend/src/snippet-parser/tests/simple.cpp; }; } 2 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/class.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser -d %s | %filecheck %s 2 | 3 | #include "somefile" 4 | #include 5 | 6 | using namespace std; 7 | 8 | class A { 9 | bool Field; 10 | // CHECK: Context[[[@LINE-1]]] 11 | // CHECK-NEXT: class A 12 | A() { 13 | cout << "A"; 14 | // CHECK: Context[[[@LINE-1]]] 15 | // CHECK-NEXT:class A 16 | // CHECK-NEXT: A() 17 | } 18 | 19 | bool operator==(const A& a) { 20 | return true; 21 | // CHECK-LABEL: Context[20] 22 | // CHECK-NEXT: [:8] class A 23 | // CHECK: [:19] bool operator== 24 | } 25 | }; -------------------------------------------------------------------------------- /example.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | //@s snip 4 | extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAMDGPUTarget() { 5 | InitializeAMDGPUTarget(); 6 | } 7 | namespace llvm { 8 | 9 | int maindef(); 10 | // line 11 | } 12 | class Something { 13 | bool doFunction() const { 14 | return true; 15 | //- snip 16 | } 17 | 18 | void longName(int param, 19 | int param2, 20 | int param3); 21 | 22 | bool operator==(const Something& other) const { 23 | return true; 24 | } 25 | void operator()(int param) { 26 | inside the function; 27 | } 28 | }; 29 | 30 | 31 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/nested-blocks.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser -d %s | %filecheck %s 2 | bool someFunction(int a, int b) { 3 | if (a == 3) { 4 | if (b == 4) { 5 | return true; 6 | // CHECK: Context[[[@LINE-1]]] 7 | // CHECK-NEXT: bool someFunction 8 | } 9 | } 10 | if (a) { 11 | return false; 12 | } else if(b == 6) { 13 | return true; 14 | } 15 | } 16 | 17 | static VersionTuple getCanonicalVersionForOS(OSType OSKind, 18 | const VersionTuple &Version); 19 | 20 | // check that there is no context here 21 | // CHECK: Context[[[@LINE+1]]] 22 | // CHECK-NEXT: end context 23 | -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/snippet-parser/build/Output/simple.cpp.script: -------------------------------------------------------------------------------- 1 | set -o pipefail;set -x;{ { set +x; } 2>/dev/null && echo 'RUN: at line 1': 'python3/home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/main.py /home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/simple.cpp | ~/llvm-project/build/bin/FileCheck /home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/simple.cpp' >&2 && { set -x; } 2>/dev/null && { python3/home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/main.py /home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/simple.cpp | ~/llvm-project/build/bin/FileCheck /home/mirasma/Projects/llvm-mips-backend/tools/snippet-parser/tests/simple.cpp; }; } 2 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/snippet-test.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser %s | %filecheck %s 2 | namespace llvm { 3 | //@s snip1 4 | 5 | // CHECK-LABEL: "id": "snip1" 6 | // CHECK: "start_lineno": [[@LINE-3]] 7 | // CHECK-DAG: "context_stack": 8 | // CHECK-DAG: "type": "NAMESPACE" 9 | bool function(char param) { 10 | return true; 11 | } 12 | // CHECK-DAG: "end_lineno": [[@LINE+1]] 13 | //- snip1 14 | } 15 | 16 | //@s snip2 17 | // CHECK-LABEL: "id": "snip2" 18 | // CHECK: "start_lineno": [[@LINE-2]] 19 | // CHECK-DAG: "end_lineno": [[@LINE+7]] 20 | // CHECK-DAG: "context_stack": 21 | // CHECK: "type": "AFTER" 22 | // CHECK-NEXT: "line": 2 23 | bool function2(char param) { 24 | return true; 25 | } 26 | //- snip2 -------------------------------------------------------------------------------- /docs/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C181hMzK.js: -------------------------------------------------------------------------------- 1 | import{S as r}from"./TableOfContents.astro_astro_type_script_index_0_lang.CKWWgpjV.js";class c extends r{set current(e){super.current=e;const t=this.querySelector(".display-current");t&&(t.textContent=e.textContent)}constructor(){super();const e=this.querySelector("details");if(!e)return;const t=()=>{e.open=!1};e.querySelectorAll("a").forEach(s=>{s.addEventListener("click",t)}),window.addEventListener("click",s=>{e.contains(s.target)||t()}),window.addEventListener("keydown",s=>{if(s.key==="Escape"&&e.open){const o=e.contains(document.activeElement);if(t(),o){const n=e.querySelector("summary");n&&n.focus()}}})}}customElements.define("mobile-starlight-toc",c); 2 | -------------------------------------------------------------------------------- /src/content/docs/section 1/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "About this section" 3 | description: In this section, we write enough code to compile a simple program 4 | sidebar: 5 | order: 1 6 | --- 7 | 8 | import { LinkCard } from "@astrojs/starlight/components"; 9 | 10 | 11 | We'll write the minimum required code to compile a simple program 12 | with a return statement with `llc -mtriple=mipsnova`. 13 | 14 | Right now, this backend uses the SelectionDAG to lower the LLVM IR. 15 | {/* I plan to use the new GlobalISel framework in the future. */} 16 | 17 | 18 | 19 | {/* The next section will add support for more instructions and some optimizations. */} 20 | -------------------------------------------------------------------------------- /src/scripts/snippets-loader.ts: -------------------------------------------------------------------------------- 1 | import type { Loader, LoaderContext} from "astro/loaders" 2 | import {z} from "astro:content" 3 | 4 | export function snippetLoader(options: {llvmRootPath: string}) : Loader { 5 | return { 6 | name: "snippets-loader", 7 | load: async(context: LoaderContext) : Promise => { 8 | context.logger.debug("====calling a logger yo") 9 | console.log("==== running yuo") 10 | // context.store.set({ 11 | // id: "some entry", 12 | // data: { 13 | // snippetsInFile: ["aslkdjf", "aslkdjf"] 14 | // } 15 | // }) 16 | }, 17 | schema: () => z.array(z.object({ 18 | snippetId: z.string() 19 | })) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ec.config.mjs: -------------------------------------------------------------------------------- 1 | import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections' 2 | import { pluginCodeOutput } from './src/scripts/expressive-code.ts'; 3 | import { pluginCodeCaption } from './src/scripts/code-caption.ts'; 4 | 5 | /** @type {import('@astrojs/starlight/expressive-code').StarlightExpressiveCodeOptions} */ 6 | export default { 7 | plugins: [pluginCodeOutput(), pluginCodeCaption(), pluginCollapsibleSections()], 8 | themes: ['dark-plus', 'light-plus'], 9 | defaultProps: { 10 | wrap: true, 11 | overridesByLang: { 12 | 'bash,sh': { preserveIndent: true} 13 | } 14 | }, 15 | styleOverrides: { 16 | textMarkers: { 17 | backgroundOpacity: '20%', 18 | defaultChroma: '30', 19 | markBackground: '#22262d' 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/incorrect_snips.cpp: -------------------------------------------------------------------------------- 1 | //@s nova-isel-lowering-cpp-1 2 | 3 | //===- NovaIselLowering.cpp - Nova DAG Lowering Implementation -----------===// 4 | #include "NovaIselLowering.h" 5 | 6 | using namespace llvm; 7 | 8 | #define DEBUG_TYPE "nova-isel" 9 | 10 | NovaTargetLowering::NovaTargetLowering(const TargetMachine &TM, 11 | const NovaSubtarget &STI) 12 | : TargetLowering(TM) {} 13 | 14 | //- nova-isel-lowering-cpp-1 15 | 16 | // //@s nova-isel-lowering-cpp-2 17 | 18 | // NovaTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, 19 | // bool isVarArg, 20 | // const SmallVectorImpl &Outs, 21 | // const SmallVectorImpl &OutVals, 22 | // const SDLoc &dl, SelectionDAG &DAG) const { 23 | 24 | // } 25 | // //- nova-isel-lowering-cpp-2 -------------------------------------------------------------------------------- /tools/snippet-parser/tests/templates.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser -d %s | %filecheck %s 2 | 3 | template class DenseMapInfo {}; 4 | class MCRegister { 5 | int id() {return 0;} 6 | }; 7 | 8 | class forward; 9 | 10 | template <> struct DenseMapInfo { 11 | static inline MCRegister getEmptyKey() { 12 | // CHECK: Context[[[@LINE]]] 13 | // CHECK-NEXT: struct DenseMapInfo { 14 | // CHECK-NEXT: static inline MCRegister getEmptyKey() { 15 | return DenseMapInfo::getEmptyKey(); 16 | } 17 | static inline MCRegister getTombstoneKey() { 18 | return DenseMapInfo::getTombstoneKey(); 19 | } 20 | static unsigned getHashValue(const MCRegister &Val) { 21 | return DenseMapInfo::getHashValue(Val.id()); 22 | } 23 | static bool isEqual(const MCRegister &LHS, const MCRegister &RHS) { 24 | return LHS == RHS; 25 | } 26 | }; 27 | 28 | // inline hashcode hash_value(const MCRegister &Reg) { 29 | // return hash_value(Reg.id()); 30 | // } -------------------------------------------------------------------------------- /src/components/CollapsibleAside.astro: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | --- 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/lit.cfg.py: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | from dotenv import load_dotenv 3 | import os 4 | 5 | env_filepath = os.path.join(os.path.dirname(__file__), '..', '..', '..', '.env') 6 | print("env_filepath: ", env_filepath) 7 | res = load_dotenv(dotenv_path=env_filepath) 8 | if not res: 9 | print("Error loading .env file") 10 | sys.exit(1) 11 | FILECHECK_PATH = os.environ.get("FILECHECK_PATH") 12 | 13 | config.name = 'SnippetParser' 14 | config.test_format = lit.formats.ShTest(True) 15 | 16 | config.suffixes = ['.cpp', '.h'] 17 | config.test_source_root = os.path.dirname(__file__) 18 | # config.test_exec_root = 19 | # go to ../build for test_exec_root 20 | build_dir = os.path.join(config.test_source_root, '..', '..', 'build') 21 | config.test_exec_root = build_dir 22 | 23 | substitutions = [ 24 | ('%parser', 25 | 'python3 ' + os.path.join(config.test_source_root, '..', 'main.py') 26 | ), 27 | (r'%filecheck', FILECHECK_PATH), 28 | ] 29 | 30 | config.substitutions.extend(substitutions) 31 | -------------------------------------------------------------------------------- /src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection , z} from 'astro:content'; 2 | import { docsLoader } from '@astrojs/starlight/loaders'; 3 | import { docsSchema } from '@astrojs/starlight/schema'; 4 | import { snippetLoader } from './scripts/snippets-loader'; 5 | import { file } from 'astro/loaders'; 6 | import { snippetType } from './util/read-snippet'; 7 | 8 | const snippets1 = defineCollection({ 9 | loader: async () => { 10 | const response = await fetch("https://restcountries.com/v3.1/all") 11 | const data = await response.json(); 12 | console.log("========running running ===========") 13 | return data.map(country => { 14 | return { 15 | id: country.cca3, 16 | ...country 17 | } 18 | }) 19 | } 20 | }) 21 | 22 | export const collections = { 23 | docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), 24 | snippets: defineCollection({ 25 | loader: file("snippets.json"), 26 | schema: snippetType 27 | }) 28 | // snippets: snippetLoader({ 29 | // llvmRootPath: "something here" 30 | // }), 31 | // something: snippets 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /src/scripts/preprocess-snippets.js: -------------------------------------------------------------------------------- 1 | // import { defineCollection } from "astro:content" 2 | 3 | const virtualCodeSnippetModule = 'virtual:code-snippets' 4 | 5 | 6 | // export default function createLogFile() { 7 | // return { 8 | // name: "create-logfile-integration", 9 | // hooks: { 10 | // 'astro:config:setup': ({ command, updateConfig }) => { 11 | // console.log("Running the logic for pre processing"); 12 | // updateConfig({ 13 | // vite: { 14 | // plugins: [{ 15 | // name: "vite-code-snippets", 16 | // resolveId(id) { 17 | // if (id === virtualCodeSnippetModule) { 18 | // return '\0' + virtualCodeSnippetModule; 19 | // } 20 | // }, 21 | // load(id) { 22 | // if (id === virtualCodeSnippetModule) { 23 | // return `export const msg = "from virtual module"`; 24 | // } 25 | // } 26 | // }] 27 | // } 28 | // }) 29 | // } 30 | // } 31 | // } 32 | // } 33 | -------------------------------------------------------------------------------- /tools/snippet-parser/tests/func-formats.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %parser -d %s | %filecheck %s 2 | // Check all formatting styles for functions. 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main() { 8 | cout << "Hello, World!" << endl; 9 | // CHECK: Context[[[@LINE-1]]]: 10 | // CHECK-NEXT: [:[[@LINE-3]]] int main 11 | auto lambda = []() { cout << "Hello, Lambda!" << endl; }; 12 | 13 | auto lambdaWithParams = [](int a, int b) { 14 | cout << "Sum: " << a + b << endl; 15 | // CHECK: Context[[[@LINE-1]]]: 16 | // CHECK-NEXT: [:7] int main 17 | }; 18 | return 0; 19 | } 20 | 21 | bool AMDGPUTargetMachine::splitModule( 22 | Module &M, unsigned NumParts, 23 | function_ref MPart)> ModuleCallback) { 24 | cout << "Hello, World!" << endl; 25 | // CHECK: Context[[[@LINE-1]]] 26 | // CHECK-NEXT: [:[[@LINE-5]]] bool AMDGPUTargetMachine::splitModule 27 | return false; 28 | } 29 | 30 | void main(int param = 0, typename something::type anotherParma) { 31 | cout << "Hello, World!" << endl; 32 | // CHECK: Context[[[@LINE-1]]] 33 | // CHECK-NEXT: [:[[@LINE-3]]] void main 34 | } 35 | 36 | extern "C" LLVM_ATTRIBUTE_WEAK void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { 37 | cout << "Hello, World!" << endl; 38 | // CHECK: Context[[[@LINE-1]]] 39 | // CHECK-NEXT: [:[[@LINE-3]]] extern "C" 40 | } 41 | 42 | bool llvm::debuginfoShouldUseDebugInstrRef(const Triple &T) { 43 | cout << "function"; 44 | // CHECK: Context[[[@LINE-1]]] 45 | // CHECK-NEXT: [:[[@LINE-3]]] 46 | } -------------------------------------------------------------------------------- /src/content/docs/background/04 Getting Started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Start by setting up your development station. 4 | sidebar: 5 | order: 4 6 | --- 7 | import {Aside} from "@astrojs/starlight/components"; 8 | 9 | {/* */} 12 | 13 | ## Grab the LLVM source 14 | Clone the official LLVM repository from GitHub. 15 | ```bash 16 | git clone https://github.com/llvm/llvm-project.git 17 | ``` 18 | 19 | ### Checkout the LLVM version 20 | Checkout the commit `7602d781b03427052c44537fa4b9c2a6da15697c`. 21 | You can create a new branch from here. 22 | ```bash 23 | cd llvm-project 24 | git checkout 7602d781b03427052c44537fa4b9c2a6da15697c 25 | git switch -c your-branch-name 26 | ``` 27 | 28 | Build the LLVM project. See all options at [Building LLVM with CMake](https://llvm.org/docs/CMake.html#frequently-used-llvm-related-variables). 29 | Here is an example: (the `Nova` target is what we will add, so it won't work yet) 30 | > Nova can be enabled after the [Target Triple chapter](/section-1/01-triple). 31 | ```bash 32 | cmake -S llvm -B build -G Ninja \ 33 | -DCMAKE_BUILD_TYPE=Release \ 34 | -DLLVM_BUILD_TESTS=ON \ 35 | -DLLVM_PARALLEL_LINK_JOBS=8 \ 36 | -DLLVM_TARGETS_TO_BUILD='X86;Mips;Nova' # Nova won't work yet 37 | 38 | cmake --build build --target llc 39 | ``` 40 | 41 | {/* ## The LLVM IR */} 42 | {/* To do. */} 43 | 44 | {/* ## Compiling with llc */} 45 | {/* To do. */} 46 | -------------------------------------------------------------------------------- /docs/sitemap-0.xml: -------------------------------------------------------------------------------- 1 | https://optimisan.github.io/llvm-mips-backendhttps://optimisan.github.io/llvm-mips-backend/background/https://optimisan.github.io/llvm-mips-backend/background/01-motivation/https://optimisan.github.io/llvm-mips-backend/background/02-mips-architecture/https://optimisan.github.io/llvm-mips-backend/background/03-about-llvm/https://optimisan.github.io/llvm-mips-backend/background/04-getting-started/https://optimisan.github.io/llvm-mips-backend/guides/example/https://optimisan.github.io/llvm-mips-backend/mips-arch/introduction/https://optimisan.github.io/llvm-mips-backend/reference/example/https://optimisan.github.io/llvm-mips-backend/reference/mips-isa/https://optimisan.github.io/llvm-mips-backend/section-1/https://optimisan.github.io/llvm-mips-backend/section-1/01-triple/https://optimisan.github.io/llvm-mips-backend/section-1/02-registers/https://optimisan.github.io/llvm-mips-backend/section-1/03-instructions/https://optimisan.github.io/llvm-mips-backend/section-1/testing/ -------------------------------------------------------------------------------- /src/content/docs/background/05-motivation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Why write this? 3 | description: Why write this tutorial? 4 | sidebar: 5 | order: 5 6 | --- 7 | 8 | There are few tutorials on writing LLVM backends, and the existing ones could get outdated quickly. 9 | 10 | The main goal was to try to document how TableGen ISel patterns work and make it easier for newcomers to 11 | pick up without just relying on experimenting and looking at exampls from other backends. 12 | 13 | {/* This guide aims to provide a comprehensive overview of how to write a backend for LLVM. */} 14 | {/* I hope to explain some things that are not well documented, and to provide a */} 15 | {/* good starting point for anyone interested in writing a backend. */} 16 | 17 | With time I aim to add enough information to make this guide approachable to people not familiar with compilers or LLVM. 18 | 19 | 20 | > Copying existing patterns and reading the comments in Target.td and TargetSelectionDAG.td are the best ways I know of learning how this works. **I haven't seen a separate guide**, although it would be very cool if one existed. 21 | > -- [llvm-dev thread](https://groups.google.com/g/llvm-dev/c/aJCR1mBC0So/m/qQKo6tk6CAAJ) 22 | 23 | > Question: "How does one learn the details of TableGen's pattern-matching syntax? 24 | > I have tried to do this, maybe a dozen times and **failed every time**." 25 | > 26 | > Answer: "...by trial and error" 27 | > 28 | > -- asked in https://www.youtube.com/watch?v=nNQ6AF6i5FI 29 | 30 | I hope it is no longer trial and error. Head on to the patterns section on the [tablegen for instruction selection page!](/llvm-cg-ref/selection-dag/02-selection-dag-td#patterns) 31 | -------------------------------------------------------------------------------- /docs/_astro/TableOfContents.astro_astro_type_script_index_0_lang.CKWWgpjV.js: -------------------------------------------------------------------------------- 1 | const g="_top";class f extends HTMLElement{constructor(){super(),this._current=this.querySelector('a[aria-current="true"]'),this.minH=parseInt(this.dataset.minH||"2",10),this.maxH=parseInt(this.dataset.maxH||"3",10),this.onIdle=e=>(window.requestIdleCallback||(o=>setTimeout(o,1)))(e),this.init=()=>{const e=[...this.querySelectorAll("a")],o=t=>{if(t instanceof HTMLHeadingElement){if(t.id===g)return!0;const s=t.tagName[1];if(s){const n=parseInt(s,10);if(n>=this.minH&&n<=this.maxH)return!0}}return!1},i=t=>{if(!t)return null;const s=t;for(;t;){if(o(t))return t;for(t=t.previousElementSibling;t?.lastElementChild;)t=t.lastElementChild;const n=i(t);if(n)return n}return i(s.parentElement)},c=t=>{for(const{isIntersecting:s,target:n}of t){if(!s)continue;const l=i(n);if(!l)continue;const m=e.find(d=>d.hash==="#"+encodeURIComponent(l.id));if(m){this.current=m;break}}},a=document.querySelectorAll("main [id], main [id] ~ *, main .content > *");let r;const u=()=>{r||(r=new IntersectionObserver(c,{rootMargin:this.getRootMargin()}),a.forEach(t=>r.observe(t)))};u();let h;window.addEventListener("resize",()=>{r&&(r.disconnect(),r=void 0),clearTimeout(h),h=setTimeout(()=>this.onIdle(u),200)})},this.onIdle(()=>this.init())}set current(e){e!==this._current&&(this._current&&this._current.removeAttribute("aria-current"),e.setAttribute("aria-current","true"),this._current=e)}getRootMargin(){const e=document.querySelector("header")?.getBoundingClientRect().height||0,o=this.querySelector("summary")?.getBoundingClientRect().height||0,i=e+o+32,c=i+53,a=document.documentElement.clientHeight;return`-${i}px 0% ${c-a}px`}}customElements.define("starlight-toc",f);export{f as S}; 2 | -------------------------------------------------------------------------------- /src/content/docs/background/02-MIPS Architecture.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Nova (or MIPS) Architecture 3 | description: Overview of the MIPS architecture we will follow. 4 | sidebar: 5 | order: 9 6 | draft: true 7 | --- 8 | 9 | The name of our new target is Nova. 10 | 11 | ## Architecture Overview 12 | 13 | The ABI we will follow is the System V ABI for MIPS32. 14 | We will follow the description of the MIPS32 assembly language given in the [MIPS 15 | Assembly Language Programmer's Guide](https://www.cs.unibo.it/~solmi/teaching/arch_2002-2003/AssemblyLanguageProgDoc.pdf) book linked below. 16 | 17 | You can find the ABI details in the [MIPS32 ABI](https://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf) document. 18 | 19 | > You don't need to read these documents right now. We will cover all the details as we build our backend. 20 | 21 | import {LinkCard, CardGrid} from "@astrojs/starlight/components"; 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | import {Aside} from "@astrojs/starlight/components"; 32 | 33 | ### Note 34 | 35 | {/* Instead of dumping out all the ISA details here, we will start building the backend and look at the asm details as we go along. It is recommended that you know the basics of any assembly programming language before you start with this book. */} 36 | It is assumed that you know the basics of assembly programming. In fact, 37 | most of the tutorial is generic and doesn't need specific MIPS knowledge. 38 | 39 | 42 | 43 | -------------------------------------------------------------------------------- /docs/_astro/page.7qqag-5g.js: -------------------------------------------------------------------------------- 1 | const d=new Set,c=new WeakSet;let f=!0,h,l=!1;function v(e){l||(l=!0,f??=!1,h??="hover",g(),p(),w(),L())}function g(){for(const e of["touchstart","mousedown"])document.body.addEventListener(e,t=>{i(t.target,"tap")&&s(t.target.href,{ignoreSlowConnection:!0})},{passive:!0})}function p(){let e;document.body.addEventListener("focusin",n=>{i(n.target,"hover")&&t(n)},{passive:!0}),document.body.addEventListener("focusout",o,{passive:!0}),u(()=>{for(const n of document.getElementsByTagName("a"))c.has(n)||i(n,"hover")&&(c.add(n),n.addEventListener("mouseenter",t,{passive:!0}),n.addEventListener("mouseleave",o,{passive:!0}))});function t(n){const r=n.target.href;e&&clearTimeout(e),e=setTimeout(()=>{s(r)},80)}function o(){e&&(clearTimeout(e),e=0)}}function w(){let e;u(()=>{for(const t of document.getElementsByTagName("a"))c.has(t)||i(t,"viewport")&&(c.add(t),e??=y(),e.observe(t))})}function y(){const e=new WeakMap;return new IntersectionObserver((t,o)=>{for(const n of t){const r=n.target,a=e.get(r);n.isIntersecting?(a&&clearTimeout(a),e.set(r,setTimeout(()=>{o.unobserve(r),e.delete(r),s(r.href)},300))):a&&(clearTimeout(a),e.delete(r))}})}function L(){u(()=>{for(const e of document.getElementsByTagName("a"))i(e,"load")&&s(e.href)})}function s(e,t){e=e.replace(/#.*/,"");const o=t?.ignoreSlowConnection??!1;if(S(e,o))if(d.add(e),document.createElement("link").relList?.supports?.("prefetch")&&t?.with!=="fetch"){const n=document.createElement("link");n.rel="prefetch",n.setAttribute("href",e),document.head.append(n)}else fetch(e,{priority:"low"})}function S(e,t){if(!navigator.onLine||!t&&m())return!1;try{const o=new URL(e,location.href);return location.origin===o.origin&&(location.pathname!==o.pathname||location.search!==o.search)&&!d.has(e)}catch{}return!1}function i(e,t){if(e?.tagName!=="A")return!1;const o=e.dataset.astroPrefetch;return o==="false"?!1:t==="tap"&&(o!=null||f)&&m()?!0:o==null&&f||o===""?t===h:o===t}function m(){if("connection"in navigator){const e=navigator.connection;return e.saveData||/2g/.test(e.effectiveType)}return!1}function u(e){e();let t=!1;document.addEventListener("astro:page-load",()=>{if(!t){t=!0;return}e()})}v(); 2 | -------------------------------------------------------------------------------- /docs/_astro/ec.8zarh.js: -------------------------------------------------------------------------------- 1 | try{(()=>{function a(e){if(!e)return;let t=e.getAttribute("tabindex")!==null,n=e.scrollWidth>e.clientWidth;n&&!t?e.setAttribute("tabindex","0"):!n&&t&&e.removeAttribute("tabindex")}var u=window.requestIdleCallback||(e=>setTimeout(e,1)),i=window.cancelIdleCallback||clearTimeout;function l(e){let t=new Set,n,r;return new ResizeObserver(c=>{c.forEach(o=>t.add(o.target)),n&&clearTimeout(n),r&&i(r),n=setTimeout(()=>{r&&i(r),r=u(()=>{t.forEach(o=>e(o)),t.clear()})},250)})}function d(e,t){e.querySelectorAll?.(".expressive-code pre > code").forEach(n=>{let r=n.parentElement;r&&t.observe(r)})}var s=l(a);d(document,s);var b=new MutationObserver(e=>e.forEach(t=>t.addedNodes.forEach(n=>{d(n,s)})));b.observe(document.body,{childList:!0,subtree:!0});document.addEventListener("astro:page-load",()=>{d(document,s)});})();}catch(e){console.error("[EC] tabindex-js-module failed:",e)} 2 | try{(()=>{function i(o){let e=document.createElement("pre");Object.assign(e.style,{opacity:"0",pointerEvents:"none",position:"absolute",overflow:"hidden",left:"0",top:"0",width:"20px",height:"20px",webkitUserSelect:"auto",userSelect:"all"}),e.ariaHidden="true",e.textContent=o,document.body.appendChild(e);let a=document.createRange();a.selectNode(e);let n=getSelection();if(!n)return!1;n.removeAllRanges(),n.addRange(a);let r=!1;try{r=document.execCommand("copy")}finally{n.removeAllRanges(),document.body.removeChild(e)}return r}async function l(o){let e=o.currentTarget,a=e.dataset,n=!1,r=a.code.replace(/\u007f/g,` 3 | `);try{await navigator.clipboard.writeText(r),n=!0}catch{n=i(r)}if(!n||e.parentNode?.querySelector(".feedback"))return;let t=document.createElement("div");t.classList.add("feedback"),t.append(a.copied),e.before(t),t.offsetWidth,requestAnimationFrame(()=>t?.classList.add("show"));let c=()=>!t||t.classList.remove("show"),d=()=>{!t||parseFloat(getComputedStyle(t).opacity)>0||(t.remove(),t=void 0)};setTimeout(c,1500),setTimeout(d,2500),e.addEventListener("blur",c),t.addEventListener("transitioncancel",d),t.addEventListener("transitionend",d)}function s(o){o.querySelectorAll?.(".expressive-code .copy button").forEach(e=>e.addEventListener("click",l))}s(document);var u=new MutationObserver(o=>o.forEach(e=>e.addedNodes.forEach(a=>{s(a)})));u.observe(document.body,{childList:!0,subtree:!0});document.addEventListener("astro:page-load",()=>{s(document)});})();}catch(e){console.error("[EC] copy-js-module failed:",e)} -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from 'astro/config'; 3 | import starlight from '@astrojs/starlight'; 4 | import { exec } from "child_process"; 5 | 6 | import tailwindcss from '@tailwindcss/vite'; 7 | 8 | // https://astro.build/config 9 | export default defineConfig({ 10 | site: "https://llvm-to-mips.netlify.app", 11 | integrations: [ 12 | { 13 | name: "create-snippets-json", 14 | hooks: { 15 | 'astro:config:setup': async (astroConfig) => { 16 | // console.log('Running create-snippets-json'); 17 | exec("./build_snippets", (err, stdout, stderr) => { 18 | if (err) { 19 | astroConfig.logger.error(`Error running create-snippets-json: ${err}`); 20 | return; 21 | } 22 | astroConfig.logger.info(`create-snippets-json: ${stdout}`); 23 | }) 24 | astroConfig.logger.info('Running create-snippets-json'); 25 | } 26 | } 27 | }, 28 | starlight({ 29 | title: 'LLVM to MIPS', 30 | customCss: ['./src/styles/custom.css'], 31 | social: { 32 | github: 'https://github.com/optimisan/llvm-mips-backend', 33 | }, 34 | sidebar: [ 35 | // { 36 | // label: 'Guides', 37 | // items: [ 38 | // // Each item here is one entry in the navigation menu. 39 | // { label: 'Example Guide', slug: 'guides/example' }, 40 | // ], 41 | // }, 42 | { 43 | label: 'About', 44 | autogenerate: { directory: 'background' }, 45 | }, 46 | { 47 | label: 'LLVM\'s CodeGen mechanics', 48 | autogenerate: { directory: 'llvm-cg-ref' }, 49 | // slug: 'llvm-codegen-ref' 50 | }, 51 | { 52 | label: "A Minimal MIPS Backend", 53 | autogenerate: { directory: "section 1" } 54 | }, 55 | { 56 | label: 'Reference', 57 | autogenerate: { directory: 'reference' }, 58 | }, 59 | ], 60 | }), 61 | ], 62 | 63 | vite: { 64 | plugins: [tailwindcss()], 65 | }, 66 | }); -------------------------------------------------------------------------------- /docs/_astro/Search.astro_astro_type_script_index_0_lang.Bp05R14F.js: -------------------------------------------------------------------------------- 1 | const y="modulepreload",w=function(h){return"/llvm-mips-backend/"+h},g={},S=function(u,i,c){let m=Promise.resolve();if(i&&i.length>0){document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),t=r?.nonce||r?.getAttribute("nonce");m=Promise.allSettled(i.map(n=>{if(n=w(n),n in g)return;g[n]=!0;const d=n.endsWith(".css"),f=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${n}"]${f}`))return;const o=document.createElement("link");if(o.rel=d?"stylesheet":y,d||(o.as="script"),o.crossOrigin="",o.href=n,t&&o.setAttribute("nonce",t),document.head.appendChild(o),d)return new Promise((e,s)=>{o.addEventListener("load",e),o.addEventListener("error",()=>s(new Error(`Unable to preload CSS for ${n}`)))})}))}function l(r){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=r,window.dispatchEvent(t),!t.defaultPrevented)throw r}return m.then(r=>{for(const t of r||[])t.status==="rejected"&&l(t.reason);return u().catch(l)})},v={ranking:{pageLength:.1,termFrequency:.1,termSaturation:2,termSimilarity:9}};class E extends HTMLElement{constructor(){super();const u=this.querySelector("button[data-open-modal]"),i=this.querySelector("button[data-close-modal]"),c=this.querySelector("dialog"),m=this.querySelector(".dialog-frame"),l=e=>{("href"in(e.target||{})||document.body.contains(e.target)&&!m.contains(e.target))&&t()},r=e=>{c.showModal(),document.body.toggleAttribute("data-search-modal-open",!0),this.querySelector("input")?.focus(),e?.stopPropagation(),window.addEventListener("click",l)},t=()=>c.close();u.addEventListener("click",r),u.disabled=!1,i.addEventListener("click",t),c.addEventListener("close",()=>{document.body.toggleAttribute("data-search-modal-open",!1),window.removeEventListener("click",l)}),window.addEventListener("keydown",e=>{(e.metaKey===!0||e.ctrlKey===!0)&&e.key==="k"&&(c.open?t():r(),e.preventDefault())});let n={};try{n=JSON.parse(this.dataset.translations||"{}")}catch{}const o=this.dataset.stripTrailingSlash!==void 0?e=>e.replace(/(.)\/(#.*)?$/,"$1$2"):e=>e;window.addEventListener("DOMContentLoaded",()=>{(window.requestIdleCallback||(s=>setTimeout(s,1)))(async()=>{const{PagefindUI:s}=await S(async()=>{const{PagefindUI:a}=await import("./ui-core.CECl5_KS.js");return{PagefindUI:a}},[]);new s({...v,element:"#starlight__search",baseUrl:"/llvm-mips-backend",bundlePath:"/llvm-mips-backend".replace(/\/$/,"")+"/pagefind/",showImages:!1,translations:n,showSubResults:!0,processResult:a=>{a.url=o(a.url),a.sub_results=a.sub_results.map(p=>(p.url=o(p.url),p))}})})})}}customElements.define("site-search",E);export{S as _}; 2 | -------------------------------------------------------------------------------- /src/scripts/expressive-code.ts: -------------------------------------------------------------------------------- 1 | import { definePlugin, AttachedPluginData } from "@expressive-code/core"; 2 | import { h } from "@expressive-code/core/hast"; 3 | 4 | interface OutputData { 5 | output: string[]; 6 | } 7 | const outputData = new AttachedPluginData(() => ({ output: [] })); 8 | 9 | export function pluginCodeOutput() { 10 | return definePlugin({ 11 | name: "Code output", 12 | baseStyles: ` 13 | .expressive-code .frame pre.output { 14 | background-color: #171a28; 15 | display: block; 16 | border: var(--ec-brdWd) solid var(--ec-brdCol); 17 | border-top: var(--ec-brdWd) dashed var(--ec-brdCol); 18 | padding: var(--ec-codePadBlk) 0; 19 | padding-inline-start: var(--ec-codePadInl); 20 | } 21 | `, 22 | hooks: { 23 | preprocessCode: (context) => { 24 | if (!context.codeBlock.meta.includes("withOutput")) return; 25 | 26 | const blockData = outputData.getOrCreateFor(context.codeBlock); 27 | const outputStart = context.codeBlock 28 | .getLines() 29 | .findIndex((line) => !line.text.startsWith("> ")); 30 | context.codeBlock 31 | .getLines(0, outputStart == -1 ? undefined : outputStart) 32 | .forEach((line) => { 33 | // remove the "> " 34 | line.editText(0, 2, ""); 35 | }); 36 | if (outputStart === -1) return; 37 | 38 | context.codeBlock.getLines(outputStart).forEach((line) => { 39 | blockData.output.push(line.text); 40 | }); 41 | 42 | for ( 43 | let i = context.codeBlock.getLines().length; 44 | i > outputStart; 45 | i-- 46 | ) { 47 | // Do this in reverse direction so there's no issue with line numbers 48 | // changing as we delete lines 49 | context.codeBlock.deleteLine(i - 1); 50 | } 51 | }, 52 | postprocessRenderedBlock: async (context) => { 53 | if (!context.codeBlock.meta.includes("withOutput")) return; 54 | 55 | const blockData = outputData.getOrCreateFor(context.codeBlock); 56 | if (!blockData.output.length) return; 57 | 58 | const lastPre = context.renderData.blockAst.children.findLastIndex( 59 | (child) => child.type === "element" && child.tagName === "pre" 60 | ); 61 | 62 | if (lastPre === -1) return; 63 | 64 | const currentChildren = context.renderData.blockAst.children; 65 | const newChildren = [ 66 | ...currentChildren.slice(0, lastPre + 1), 67 | h( 68 | "pre.output", 69 | blockData.output.map((line) => h("div", line)) 70 | ), 71 | ...currentChildren.slice(lastPre + 1), 72 | ]; 73 | context.renderData.blockAst.children = newChildren; 74 | }, 75 | }, 76 | }); 77 | } -------------------------------------------------------------------------------- /src/content/docs/reference/mips-arch.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MIPS Architecture 3 | description: Learn about the MIPS instruction set, register file organization, calling conventions, and special hardware features that influence backend implementation. 4 | --- 5 | 6 | For reference, read Volume IA. 7 | [Quick reference](https://web.archive.org/web/20200730010546/https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00565-2B-MIPS32-QRC-01.01.pdf) 8 | 9 | https://pages.cs.wisc.edu/~markhill/cs354/Fall2008/beyond354/conventions.html 10 | 11 | MIPS ABI: https://dmz-portal.mips.com/wiki/MIPS_ABI 12 | 13 | Much of this information is taken from the [System V Application Binary Interface (MIPS RISC Processor)](https://sourceware.org/legacy-ml/binutils/2003-06/msg00436.html). 14 | 15 | ABI Document: https://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf 16 | 17 | ## Data representation 18 | 19 | A byte is 8 bits. A halfword is 16 bits. A word is 32 bits. A doubleword is 64 bits. 20 | We follow the big-endian convention, where the most significant byte is stored at the lowest address. 21 | 22 | ## CPU Registers 23 | 24 | - 32 32-bit general purpose registers 25 | - 2 special 32-bit registers to hold the results of multiplication and division. 26 | - 1 32-bit program counter (pc) 27 | 28 | The general registers are named `$0` to `$31`. 29 | 30 | ### Special CPU registers 31 | 32 | ### Register usage conventions 33 | 34 | | Register Name | Software name | Use | 35 | | ------------- | ------------- | ------------------------------------ | 36 | | $0 | $zero | Hard-wired to 0 | 37 | | $1 | $at | Reserved for assembler | 38 | | $2-$3 | $v0-$v1 | Return values from functions | 39 | | $4-$7 | $a0-$a3 | Arguments to functions | 40 | | $8-$15 | $t0-$t7 | Temporary registers | 41 | | $16-$23 | $s0-$s7 | Saved registers (saved across calls) | 42 | | $24-$25 | $t8-$t9 | More temporary registers | 43 | | $26-$27 | $k0-$k1 | Reserved for kernel | 44 | | $28 | $gp | Global pointer | 45 | | $29 | $sp | Stack pointer | 46 | | $30 | $s8/$fp | Frame pointer | 47 | | $31 | $ra | Return address | 48 | 49 | Only $16-$23 and $28-30 are saved across function calls. Register $28 is not preserved when calling position independent code. 50 | 51 | > More on $gp 52 | > The global pointer points to the middle of a 64KB block of memory in the heap that holds constants and global variables. Objects in this heap can be quickly accessed with a single load or store instruction. The global pointer is set by the operating system when the program starts. 53 | -------------------------------------------------------------------------------- /mips-code/mips1.asm: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .globl main 4 | .data 5 | n: .word 4 # number of test cases 6 | insp: .word 0, 5, 10, 100 # input numbers 7 | outs: .word 0, 170, 2640, 25164150 # expected result 8 | failmsg: .asciiz "failed for test input: " 9 | expectedmsg: .asciiz ". expected " 10 | tobemsg: .asciiz " to be " 11 | okmsg: .asciiz "all tests passed" 12 | .text 13 | runner: 14 | lw $s0, n 15 | la $s1, insp 16 | la $s2, outs 17 | run_test: 18 | lw $s3, 0($s1) # read input from memory 19 | move $a0, $s3 # move it to a0 20 | jal main # call subroutine under test 21 | move $v1, $v0 # move return value in v0 to v1 because we need v0 for syscall 22 | lw $s4, 0($s2) # read expected output from memory 23 | bne $v1, $s4, exit_fail # if expected doesn't match actual, jump to fail 24 | addi $s1, $s1, 2+2 # move to next word in input 25 | addi $s2, $s2, 4 # move to next word in output 26 | sub $s0, $s0, 1 # decrement num of tests left to run 27 | bgt $s0, $zero, run_test # if more than zero tests to run, jump to run_test 28 | exit_ok: 29 | la $a0, okmsg # put address of okmsg into a0 30 | li $v0, 4 # 4 is print string 31 | syscall 32 | li $v0, 10 # 10 is exit with zero status (clean exit) 33 | syscall 34 | exit_fail: 35 | la $a0, failmsg # put address of failmsg into a0 36 | li $v0, 4 # 4 is print string 37 | syscall 38 | move $a0, $s3 # set arg of syscall to input that failed the test 39 | li $v0, 1 # 1 is print int 40 | syscall 41 | la $a0, expectedmsg 42 | li $v0, 4 43 | syscall 44 | move $a0, $v1 # print actual that failed on 45 | li $v0, 1 46 | syscall 47 | la $a0, tobemsg 48 | li $v0, 4 49 | syscall 50 | move $a0, $s4 # print expected value that failed on 51 | li $v0, 1 52 | syscall 53 | li $a0, 1 # set exit code to 1 54 | li $v0, 17 # terminate with the exit code in $a0 55 | syscall 56 | 57 | li $t0, 1 58 | li $t5, 0 59 | mthi $t5 60 | mtlo $t5 61 | move $v0, $0 62 | bnez $a0, start 63 | li $v0, 0 64 | jr $ra 65 | start: addiu $a0, $a0, 1 66 | while1: 67 | beq $t0, $a0, end 68 | nop 69 | body: 70 | addu $t1, $t1, $t0 71 | maddu $t0, $t0 72 | # increment 73 | addiu $t0, $t0, 1 74 | j while1 75 | end: 76 | mflo $v0 #sum of sq 77 | mulu $t2, $t1, $t1 #sq of sum 78 | sub $v0, $t2, $v0 79 | jr $ra -------------------------------------------------------------------------------- /src/components/InlineComment.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // This component renders the slot content (which should be only a text node) 3 | // and adds a dotted underline to it with an icon next to it. 4 | // When hovered, it shows the comment text in a tooltip. 5 | interface Props { 6 | comment: string; 7 | type?: 'note' | 'tip' | 'warning' | 'danger'; 8 | } 9 | 10 | const { comment, type = 'note' } = Astro.props as Props; 11 | 12 | const icons = { 13 | note: ``, 14 | tip: ``, 15 | warning: ``, 16 | danger: `` 17 | }; 18 | 19 | --- 20 | 21 | 80 | 81 |
82 | 83 | 84 |
85 | -------------------------------------------------------------------------------- /src/scripts/code-caption.ts: -------------------------------------------------------------------------------- 1 | import { definePlugin, AttachedPluginData } from "@expressive-code/core"; 2 | import { h } from "@expressive-code/core/hast"; 3 | import { fromMarkdown } from "mdast-util-from-markdown"; 4 | import { toHast } from "mdast-util-to-hast"; 5 | 6 | interface CaptionData { 7 | caption?: string; 8 | } 9 | const captionData = new AttachedPluginData(() => ({})); 10 | 11 | export function pluginCodeCaption() { 12 | return definePlugin({ 13 | name: "Code caption", 14 | baseStyles: ` 15 | figcaption:last-child { 16 | background-color: black; 17 | }`, 18 | hooks: { 19 | preprocessCode: (context) => { 20 | const allLines = [...context.codeBlock.getLines()]; 21 | if (allLines.length && allLines[allLines.length - 1]?.text !== "---") { 22 | return; 23 | } 24 | const captionStartLine = allLines.findLastIndex((line, index) => { 25 | // Let's skip the actual last match 26 | if (index === allLines.length - 1) return false; 27 | return line.text === "---"; 28 | }); 29 | if (!captionStartLine) return; 30 | 31 | const blockData = captionData.getOrCreateFor(context.codeBlock); 32 | blockData.caption = allLines 33 | .slice(captionStartLine + 1, allLines.length - 1) 34 | .map((line) => line.text) 35 | .join("\n"); 36 | 37 | for (let i = allLines.length; i > captionStartLine; i--) { 38 | // Do this in reverse direction so there's no issue with line numbers 39 | // changing as we delete lines 40 | context.codeBlock.deleteLine(i - 1); 41 | } 42 | // Also delete the last line if it remains empty 43 | 44 | if (context.codeBlock.getLines().length > 0) { 45 | const lastLine = 46 | context.codeBlock.getLines()[ 47 | context.codeBlock.getLines().length - 1 48 | ]; 49 | if (lastLine && lastLine.text.trim() === "") { 50 | context.codeBlock.deleteLine( 51 | context.codeBlock.getLines().length - 1 52 | ); 53 | } 54 | } 55 | }, 56 | postprocessRenderedBlockGroup: async (context) => { 57 | // Find the block that has a caption, if any 58 | const captionBlock = context.renderedGroupContents.find( 59 | (groupContent) => { 60 | const blockData = captionData.getOrCreateFor( 61 | groupContent.codeBlock 62 | ); 63 | return !!blockData.caption; 64 | } 65 | ); 66 | if (!captionBlock) { 67 | return; 68 | } 69 | const root = context.renderData.groupAst; 70 | // turn the div into a figure 71 | root.tagName = "figure"; 72 | // add a figcaption child with the caption 73 | root.children.push( 74 | h( 75 | "figcaption", 76 | toHast( 77 | fromMarkdown( 78 | captionData.getOrCreateFor(captionBlock.codeBlock).caption! 79 | ) 80 | ) 81 | ) 82 | ); 83 | }, 84 | }, 85 | }); 86 | } -------------------------------------------------------------------------------- /src/content/docs/background/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: About this tutorial 3 | description: "" 4 | sidebar: 5 | order: 1 6 | --- 7 | This tutorial will go over the process of converting LLVM IR to MIPS assembly code. 8 | 9 | > Right now this only covers the return instruction. 10 | 11 | Specifically, we will only be working on `llc`, the static LLVM IR to assembly compiler. 12 | 13 | import {LinkCard} from "@astrojs/starlight/components"; 14 | 15 | 16 | 17 | We will add a new target named Nova to the LLVM tree for the MIPS architecture. Like [Crafting Interpreters](https://craftinginterpreters.com), every line has an explanation in this series so you can follow along seamlessly. 18 | 19 | > The main contribution of this tutorial is the pattern matching syntax for tablegen, [given here](/llvm-cg-ref/selection-dag/02-selection-dag-td/) 20 | 21 | 22 | ## Reader's guide 23 | The finished code for this backend is at the [nova-backend branch of the fork](https://github.com/optimisan/llvm-project/tree/nova-backend). 24 | 25 | ### Running commands 26 | All bash commands are to be run from the root of the LLVM source tree, 27 | named as `llvm-project`. 28 | 29 | 34 | 35 | ```bash title="Example setup" 36 | git clone https://github.com/llvm/llvm-project 37 | cd llvm-project 38 | ``` 39 | 40 | ### Command outputs 41 | For some commands I include the output that I 42 | get from it, which is displayed like so: 43 | 44 | 45 | 46 | ```bash withOutput title="Output below the command(s)" 47 | > echo "Hello World!" 48 | > echo "Output is after this code block" 49 | 50 | Hello World! 51 | Output is after this code block 52 | ``` 53 | 54 | {/* ## Code Snippets 55 | Code snippets are displayed in diff style shown below. Sometimes some sections are collapsed 56 | and can be expanded by clicking on the `X collapsed lines` indicator. 57 | 58 | The snippet's file path is shown above the code. Above the filename is the context, 59 | which can be the lexical scope or an action. For example, 60 | the context below implies that a new file should be created for this snippet. 61 | 62 | import CodeSnippet from "../../../components/CodeSnippet.astro"; 63 | 64 | */} 65 | 66 | ## Errata 67 | If you find any errors or have any suggestions, please let me know by creating an issue on the book's GitHub repository. PRs are also most welcome! 68 | 69 | 70 | {/* ## About the author */} 71 | {/* I am a compiler apprentice, */} 72 | {/* I am a compiler engineer at AMD, still a novice in the field. */} 73 | {/* I am interested in compilers and programming languages, and I want more 74 | people to learn about them. */} 75 | 76 | Since I am also new to LLVM, I am writing this for myself and others to learn about it. 77 | I hope you find it useful enough! -------------------------------------------------------------------------------- /src/components/BlurCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from "@astrojs/starlight/components"; 3 | import type { StarlightIcon } from "@astrojs/starlight/types"; 4 | 5 | interface Props { 6 | icon?: StarlightIcon; 7 | title: string; 8 | } 9 | const { icon, title } = Astro.props; 10 | --- 11 | 12 |
13 |

14 | {icon && } 15 | 16 |

17 |
18 |
19 | 20 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLVM to MIPS 2 | 3 | This repository contains the source for the tutorial series on writing a 4 | backend for LLVM that compiles to MIPS Release 1 assembly. 5 | 6 | This serves as a guide to anyone interested in developing a backend for LLVM. 7 | 8 | The name of this backend is `Nova`, just to distinguish from the already existing 9 | `Mips` backend. (Most of the code is derived from that) 10 | 11 | [![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) 12 | 13 | ## Running locally 14 | The code snippets for the pages are extracted and embedded into 15 | the webpage source by reading code enclosed within special 16 | snippet comments in the LLVM source. 17 | 18 | This repository has currently checked in the `snippets.json` file 19 | that stores all these extracted snippets, but this might change later. 20 | (in fact, probably *should* change later) 21 | 22 | You can skip steps 1 and 4 if you just want to build and develop the 23 | website content without changing the code for the Nova backend. 24 | 25 | 1. Clone the llvm-project directory. 26 | ```bash 27 | cd llvm-mips-tutorial # or whatever you name this 28 | # book-tutorial-project's directory 29 | git clone --single-branch --branch nova-backend https://github.com/optimisan/llvm-project.git 30 | ``` 31 | 32 | 2. Clone this repository. 33 | ```bash 34 | cd llvm-mips-tutorial 35 | git clone https://github.com/optimisan/llvm-mips-backend.git 36 | ``` 37 | 38 | 3. Copy (or move) the `.env.example` to `.env`. 39 | ```bash 40 | cd llvm-mips-tutorial/llvm-mips-backend # cd into this project 41 | cp .env.example .env 42 | open_with_editor .env 43 | ``` 44 | 45 | Enter the absolute path to the `llvm-project` folder, for example, like this: 46 | ```env 47 | # in file .env 48 | LLVM_ROOT_DIR=/home/username/llvm-mips-tutorial/llvm-project 49 | ``` 50 | 51 | 4. Build the snippets. Requires python and the `python-dotenv` package. 52 | ```bash 53 | # optional: pip3 install python-dotenv 54 | ./build-snippets 55 | ``` 56 | 57 | 5. Build and view the website! 58 | ```bash 59 | npm install 60 | npm run dev 61 | ``` 62 | 63 | Open `localhost:4321` in your browser. 64 | 65 | ## Snippets 66 | Look at [tools/snippet-parser/README.md](tools/snippet-parser/README.md) for the snippets syntax. 67 | 68 | ## Other stuff that Astro added to this README 69 | | Command | Action | 70 | | :------------------------ | :----------------------------------------------- | 71 | | `npm install` | Installs dependencies | 72 | | `npm run dev` | Starts local dev server at `localhost:4321` | 73 | | `npm run build` | Build your production site to `./dist/` | 74 | | `npm run preview` | Preview your build locally, before deploying | 75 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 76 | | `npm run astro -- --help` | Get help using the Astro CLI | 77 | 78 | ## 👀 Want to learn more? 79 | 80 | Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). 81 | -------------------------------------------------------------------------------- /tools/snippet-parser/README.md: -------------------------------------------------------------------------------- 1 | # Snippet Parser 2 | The file `main.py` will read all snippets in the llvm dir on files listed by 3 | `git diff main --name-only`. 4 | 5 | ## Running the snippet parser 6 | `main.py` either runs on the LLVM source repo (whose must be in the `.env` file), 7 | or can operate on a single file provided on the command line like so: 8 | 9 | ```bash 10 | cd llvm-mips-tutorial 11 | cd tools/snippet-parser 12 | python3 main.py input.cpp [-d] [-o output.json] 13 | ``` 14 | 15 | ### Testing 16 | There are a few tests written using the `llvm-lit` and `FileCheck` tool to 17 | test the parser. Look at `lit.cfg.py`. 18 | 19 | > You will need the `lit` and `FileCheck` tool for this. Here is what I did: 20 | > ```bash 21 | > pip install --user lit # to install lit 22 | > cd ../llvm-project 23 | > cmake --build build --target FileCheck 24 | > ``` 25 | 26 | Enter the path to the `FileCheck` binary in the `.env` file in the 27 | variable `FILECHECK_PATH`. 28 | ```env 29 | FILECHECK_PATH="/path/to/bin/FileCheck" 30 | ``` 31 | Then you can run the lit tests: 32 | ```bash 33 | cd llvm-mips-tutorial # root of this project 34 | lit -sv tools/snippet-parser/tests 35 | ``` 36 | 37 | ## Syntax 38 | Snippets are code blocks enclosed by special comments prefixed by `@s` (to start 39 | a snippet) and `-` (to end the snippet). 40 | 41 | For example, for `C++` files this is a valid snippet: 42 | ```cpp 43 | //@s snippet-name 44 | function codeHere() { return value; } 45 | //- snippet-name 46 | ``` 47 | 48 | For `CMakeLists.txt` files, use `#` for the comments as usual. 49 | ```py 50 | #@s snippet 51 | add_executable(main.cpp) 52 | #- snippet 53 | ``` 54 | 55 | Snippets can take a type, specified after the snippet name. 56 | ``` 57 | = | 58 | = "replace" "=" 59 | = [^\s=] 60 | = "delete" | "commented" | "mark" | "end" 61 | ``` 62 | Types are listed below. 63 | 64 | ### `delete` 65 | For code that is supposed to be deleted, comment it out and use the 66 | `delete` type on that snippet. This will be shown as a subtractive diff. 67 | ```cpp 68 | #include "Nova.h" 69 | //@s removed-include delete 70 | // #include "NoLongerNeeded.h" 71 | //- removed-include 72 | #include "Other.h" 73 | ``` 74 | will show as 75 | ```diff 76 | #include "Nova.h" 77 | - #include "NoLongerNeeded.h" 78 | #include "Other.h" 79 | ``` 80 | 81 | ### `commented` 82 | The snippet will be un-commented and shown as a normal snippet. 83 | (with the additive diff) 84 | 85 | ```cpp 86 | //@s snippet commented 87 | // int X = constant(); 88 | // return func(X+a); 89 | //- snippet 90 | ``` 91 | 92 | ### `mark` 93 | With `mark`, the snippet will be highlighted with a mark range intead of 94 | a diff addition range. (use this for highlighting code that is not to be 95 | added or removed) 96 | 97 | ### `end` 98 | Type `end` means no after-context will be shown in the final code snippet. 99 | 100 | ### `replace=snip-id` 101 | This indicates that the current snippet replaces the snippet 102 | marked with `snip-id`. This will show `snip-id` with the removal 103 | diff style and the current snippet as addition. 104 | The replaced snippet should be commented-out code. 105 | 106 | -------------------------------------------------------------------------------- /docs/_astro/print.BJ0teN4y.css: -------------------------------------------------------------------------------- 1 | @media print{:root[data-theme]:is(:root){--sl-color-white: hsl(224, 10%, 10%);--sl-color-gray-1: hsl(224, 14%, 16%);--sl-color-gray-2: hsl(224, 10%, 23%);--sl-color-gray-3: hsl(224, 7%, 36%);--sl-color-gray-4: hsl(224, 6%, 56%);--sl-color-gray-5: hsl(224, 6%, 77%);--sl-color-gray-6: hsl(224, 20%, 94%);--sl-color-gray-7: hsl(224, 19%, 97%);--sl-color-black: hsl(0, 0%, 100%);--sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);--sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%);--sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%);--sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%);--sl-color-green: hsl(var(--sl-hue-green), 90%, 46%);--sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%);--sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%);--sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%);--sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%);--sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%);--sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%);--sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%);--sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%);--sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);--sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);--sl-color-accent-high: hsl(234, 80%, 30%);--sl-color-accent: hsl(234, 90%, 60%);--sl-color-accent-low: hsl(234, 88%, 90%);--sl-color-text-accent: var(--sl-color-accent);--sl-color-text-invert: var(--sl-color-black);--sl-color-bg-nav: var(--sl-color-gray-7);--sl-color-bg-sidebar: var(--sl-color-bg);--sl-color-bg-inline-code: var(--sl-color-gray-6);--sl-color-bg-accent: var(--sl-color-accent);--sl-color-hairline-light: var(--sl-color-gray-6);--sl-color-hairline-shade: var(--sl-color-gray-6);--sl-color-backdrop-overlay: hsla(225, 9%, 36%, .66);--sl-shadow-sm: none;--sl-shadow-md: none;--sl-shadow-lg: none}.print\:hidden{display:none!important}.print\:flex{display:flex!important}.print\:block{display:block!important}main{padding-bottom:0!important}main>.content-panel{padding-block-start:0!important}.content-panel+.content-panel{border:0!important}.page>header{position:relative!important}.page>.main-frame{padding-top:0;padding-inline-start:0}.main-pane{--sl-sidebar-width: 0px !important;--sl-content-width: 100% !important}.sl-banner{--sl-color-banner-text: var(--sl-color-white) !important;background-color:transparent!important}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6){break-after:avoid}.sl-markdown-content :is(p,li){orphans:2;widows:2}.sl-markdown-content pre{overflow-x:hidden!important;white-space:pre-wrap!important}.sl-markdown-content .expressive-code,.sl-markdown-content figure,.sl-markdown-content pre{break-inside:avoid}.expressive-code .frame.is-terminal .header:before{box-shadow:inset 99rem 99rem var(--sl-color-gray-5)}.expressive-code .frame.has-title:not(.is-terminal) .header{background:transparent!important;border-bottom:1px solid var(--sl-color-gray-6)!important}.expressive-code .frame.has-title:not(.is-terminal) .title{background:transparent!important}.expressive-code .frame.has-title:not(.is-terminal) .title:after{border-top:0!important}.expressive-code .copy{display:none!important}.sl-markdown-content code:not(:where(.not-content *)){background-color:transparent!important;padding:0!important;margin-block:unset!important;font-size:.9375em!important}.sl-badge{background:transparent!important;color:var(--sl-color-white)!important}starlight-file-tree{break-inside:avoid}starlight-file-tree .highlight{outline:3px solid var(--sl-color-accent-low);color:var(--sl-color-text)!important;background-color:transparent!important}.starlight-aside{break-inside:avoid}.sl-link-button.primary{background:transparent!important;border-color:var(--sl-color-white)!important;color:var(--sl-color-white)!important}starlight-tabs{break-inside:avoid}.sl-steps>li:after{box-shadow:inset 99rem 99rem var(--sl-color-hairline-light)}} 2 | -------------------------------------------------------------------------------- /src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Building an LLVM Backend for MIPS 3 | description: Write your own LLVM backend for MIPS from scratch! 4 | template: splash 5 | hero: 6 | tagline: Build a backend for LLVM from scratch! 7 | # image: 8 | # file: ../../assets/houston.webp 9 | actions: 10 | - text: Let's start building! 11 | link: /background/ 12 | icon: right-arrow 13 | - text: Read about LLVM 14 | link: https://llvm.org/ 15 | icon: external 16 | variant: secondary 17 | --- 18 | 19 | import { Card, CardGrid } from "@astrojs/starlight/components"; 20 | import BlurCard from "../../components/BlurCard.astro"; 21 | 22 |
23 |
24 | 25 | 26 | Build your first LLVM backend in a weekend! Find every step lucidly explained in the pages that follow. 27 | 28 | 29 | {/* 30 | Learn the fundamental concepts of LLVM backends, including TableGen, target description files, and the overall architecture of LLVM's code generation infrastructure. 31 | */} 32 | 33 | {" "} 34 | 35 | Understand how to define patterns in TableGen for instruction selection without guesswork or 36 | having to read the source code (although you probably should, lest I've missed something). 37 | 38 | 39 | {/* */} 40 | {/* ### 2. MIPS Architecture Deep Dive Explore the MIPS instruction set */} 41 | {/* Learn the MIPS architecture, including its instruction set and calling conventions. */} 42 | {/* */} 43 | 44 | {/* */} 45 | {/* 46 | ### 3. Instruction Selection Master the process of translating LLVM IR to MIPS 47 | assembly, including pattern matching, custom TableGen patterns, and 48 | optimization opportunities specific to MIPS. 49 | 50 | 51 | {" "} 52 | 53 | 54 | ### 4. Register Allocation Implement efficient register allocation strategies 55 | for MIPS, handling register classes, spilling, and calling convention 56 | requirements. 57 | 58 | 59 | {" "} 60 | 61 | 62 | ### 5. Code Emission and ABI Learn how to emit proper MIPS assembly code, 63 | manage relocations, and ensure compliance with the MIPS ABI specifications. 64 | 65 | 66 | {" "} 67 | 68 | 69 | ### 6. Optimization Passes Develop MIPS-specific optimization passes to 70 | improve code quality, including instruction scheduling, peephole 71 | optimizations, and delay slot filling. 72 | 73 | 74 | {" "} 75 | 76 | 77 | ### 7. Testing and Debugging Set up comprehensive testing infrastructure using 78 | LLVM's integrated testing tools, and learn debugging techniques for backend 79 | development. 80 | 81 | 82 | 83 | ### 8. Performance Tuning 84 | Fine-tune the backend for optimal performance, including instruction selection patterns, scheduling policies, and target-specific optimizations. 85 | */} 86 | 87 |
88 | {/* 89 | 90 | 91 | Edit `src/content/docs/index.mdx` to see this page change. 92 | 93 | 94 | Add Markdown or MDX files to `src/content/docs` to create new pages. 95 | 96 | 97 | Edit your `sidebar` and other config in `astro.config.mjs`. 98 | 99 | 100 | Learn more in [the Starlight Docs](https://starlight.astro.build/). 101 | 102 | */} 103 | -------------------------------------------------------------------------------- /src/content/docs/reference/llvm-backend.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: LLVM backend resources 3 | description: Talks, blogs, slides on LLVM backend structures. 4 | --- 5 | 6 | ## Backend 7 | ### From LLVM 8 | Official LLVM backend guide: https://llvm.org/docs/WritingAnLLVMBackend.html 9 | 10 | ### Talks and slides 11 | Slides on Selection DAG: https://llvm.org/devmtg/2024-10/slides/tutorial/MacLean-Fargnoli-ABeginnersGuide-to-SelectionDAG.pdf 12 | 13 | Slides on building an LLVM Backend, has all the basics. 14 | https://llvm.org/devmtg/2014-04/PDFs/Talks/Building%20an%20LLVM%20backend.pdf 15 | 16 | TableGen [slide on fosdem.org](https://archive.fosdem.org/2019/schedule/event/llvm_tablegen/attachments/slides/3304/export/events/attachments/llvm_tablegen/slides/3304/tablegen.pdf) 17 | 18 | Dealing with register hierarchies https://llvm.org/devmtg/2016-11/Slides/Braun-DealingWithRegisterHierarchies.pdf 19 | 20 | ### Articles and blogs 21 | A guide just like this one. 22 | https://sourcecodeartisan.com/2020/09/13/llvm-backend-0.html 23 | 24 | Detailed guide https://jonathan2251.github.io/lbd/TutorialLLVMBackendCpu0.pdf 25 | The ebook version: https://jonathan2251.github.io/lbt/index.html 26 | 27 | A great overview of SelectionDAG https://eli.thegreenplace.net/2013/02/25/a-deeper-look-into-the-llvm-code-generator-part-1 28 | 29 | ### Commits 30 | Parameterized register class information https://reviews.llvm.org/D31951?id=113075 31 | 32 | ### Mailing List 33 | Simpler types in TableGen isel patterns https://groups.google.com/g/llvm-dev/c/RsiiCoV4U3Q/m/wVgeXn31CIQJ 34 | 35 | Debugging vector instruction operands https://lists.llvm.org/pipermail/llvm-dev/2016-December/107784.html 36 | 37 | SelectionDAG and GlobalISel discussion https://groups.google.com/g/llvm-dev/c/lAp7QFJfltY 38 | 39 | **From [this llvm thread](https://groups.google.com/g/llvm-dev/c/aJCR1mBC0So/m/yZipPTzTCAAJ)** 40 | > "Lessons in TableGen" 41 | > FOSDEM 2019; Nicolai Hähnle 42 | > https://fosdem.org/2019/schedule/event/llvm_tablegen/ 43 | > Slides: 44 | > https://archive.fosdem.org/2019/schedule/event/llvm_tablegen/attachments/slides/3304/export/events/attachments/llvm_tablegen/slides/3304/tablegen.pdf 45 | > 46 | > Series: 47 | > - What has TableGen ever done for us?: 48 | > http://nhaehnle.blogspot.com/2018/02/tablegen-1-what-has-tablegen-ever-done.html 49 | > - Functional Programming: 50 | > http://nhaehnle.blogspot.com/2018/02/tablegen-2-functional-programming.html 51 | > - Bits: http://nhaehnle.blogspot.com/2018/02/tablegen-3-bits.html 52 | > - Resolving variables: 53 | > http://nhaehnle.blogspot.com/2018/03/tablegen-4-resolving-variables.html 54 | > - DAGs: http://nhaehnle.blogspot.com/2018/03/tablegen-5-dags.html 55 | > 56 | > Some of the parts of TableGen used in SelectionDAG are in the backend 57 | > docs (e.g., the keywords OP asked about): 58 | > https://llvm.org/docs/WritingAnLLVMBackend.html#instruction-set 59 | > & https://llvm.org/docs/WritingAnLLVMBackend.html#instruction-selector 60 | > (has a simple example of `PatFrag` for `store`). 61 | > 62 | > There are a few examples of simple .td files an LLVM backend in the 63 | > following: 64 | > 65 | > LLVM backend development by example (RISC-V) 66 | > 2018 LLVM Developers’ Meeting; Alex Bradbury 67 | > https://www.youtube.com/watch?v=AFaIP-dF-RA 68 | > 69 | > 2014 - Building an LLVM Backend - LLVM Developer's Meeting 70 | > https://llvm.org/devmtg/2014-10/#tutorial1 71 | > https://llvm.org/devmtg/2014-10/Slides/Cormack-BuildingAnLLVMBackend.pdf 72 | > https://llvm.org/devmtg/2014-04/PDFs/Talks/Building%20an%20LLVM%20backend.pdf 73 | > http://web.archive.org/http://llvm.org/devmtg/2014-10/Videos/Building%20an%20LLVM%20backend-720.mov 74 | > http://llvm.org/devmtg/2014-10/#tutorial1 75 | > http://www.inf.ed.ac.uk/teaching/courses/ct/other/LLVMBackend-2015-03-26_v2.pdf 76 | > 77 | > llvm-leg: LEG Example Backend 78 | > LEG Example Backend: a simple example LLVM backend for an ARM-like 79 | > architecture: 'LEG'. 80 | > https://github.com/frasercrmck/llvm-leg 81 | 82 | ## Other stuff 83 | > May not be related to LLVM 84 | 85 | https://github.com/edrosten/autoconf_tutorial 86 | -------------------------------------------------------------------------------- /src/content/docs/section 1/03 setting up.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Setting up the Nova target 3 | description: Add the minimum required configuration code for the Nova target. 4 | sidebar: 5 | order: 4 6 | --- 7 | 8 | import {Aside} from "@astrojs/starlight/components"; 9 | import CodeSnippet from "../../../components/CodeSnippet.astro"; 10 | 11 | Before we can start adding instructions, we need to set up some required configuration code. 12 | This contains the Nova subtarget, register and instruction information, frame and selection DAG 13 | lowering classes and a few others that set up the code generation pipeline. 14 | 15 | The code we add here will allow us to build LLVM for the Nova target but not to compile 16 | any LLVM IR to assembly code yet. 17 | 18 | 23 | 24 | ## Adding the NovaSubtarget 25 | TableGen generates the `NovaGenSubtarget` class. Add the tablegen invocation to 26 | CMakeLists.txt: 27 | 28 | 29 | 30 | Whip up a new header file for the subtarget class in `lib/Target/Nova`. Here we 31 | include the generated subtarget file from TableGen. 32 | 33 | The Subtarget is the central place from where we access the target information 34 | per function. We will include some header files that we will create later on. 35 | 36 | 37 | Add the fields for all info classes. These will be created ahead. 38 | 39 | 40 | Add the corresponding getters and the constructor, ending the file. 41 | 42 | 43 | ### Global functions 44 | Targets have a few global functions to define or create codegen passes which 45 | are consolidated in one header file. Create the `Nova.h` file for our target. 46 | This is empty for now. 47 | 48 | 49 | 50 | The definitions need to be added to the implementation file. 51 | 52 | 53 | ### Registering the subtarget 54 | Include the generated file in `NovaMCTargetDesc` files. 55 | 56 | 57 | 58 | 59 | This needs the definition of the `MCSubtargetInfo` class which is the base. 60 | 61 | 62 | We need to register the subtarget in `NovaTargetMachine.cpp` as well. 63 | 64 | 65 | 66 | 67 | ## MCTargetDesc 68 | The common place for the target descriptions is the `NovaMCTargetDesc.h` file. 69 | This has the instruction set, register info and subtarget information. 70 | 71 | We already created this file in the registers section. 72 | 73 | ## TargetObjectFile 74 | Another class that is required but unused is `NovaTargetObjectFile`. 75 | 76 | 77 | Write the `Initialize` definition. 78 | 79 | 80 | ## TargetMachine 81 | The `TargetMachine` class is the main entry point for the target. It contains 82 | the codegeneration pipeline and other target information. 83 | 84 | 85 | Add the class declaration. 86 | 87 | 88 | `createPassConfig` will be added in the next section. 89 | 98 | 99 | ## ASMInfo 100 | The `MCAsmInfo` class holds the information about the assembly language for the target. 101 | 102 | I am adding the minimum possible stuff to get us running, but you can 103 | explore all the fields in the class and customize it here. 104 | 105 | 106 | Like with other target info classes, we need to register the `MCAsmInfo` class 107 | in the `NovaMCTargetDesc` file. This is done in the `Initialize` function. 108 | 109 | Include the headers. 110 | 111 | 112 | Ready the MCAsmInfo instance. 113 | 114 | Place it into the Target. 115 | 116 | 117 | 118 | Lastly, don't forget to add the source files to CMakeLists.txt. 119 | 120 | With this, we are set to implement instructions. 121 | 122 | -------------------------------------------------------------------------------- /src/styles/custom.css: -------------------------------------------------------------------------------- 1 | root { 2 | --sl-color-accent-low: #1e293b; 3 | --sl-color-accent: #3b82f6; 4 | --sl-color-accent-high: #dbeafe; 5 | --sl-font-system: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", 6 | Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 7 | } 8 | 9 | /* Light mode gradient colors */ 10 | :root[data-theme="light"] .page-gradient { 11 | background: linear-gradient( 12 | 180deg, 13 | #f0f9ff 0%, 14 | rgba(239, 246, 255, 0.8) 25%, 15 | rgba(219, 234, 254, 0.6) 50%, 16 | rgba(191, 219, 254, 0.4) 75%, 17 | rgba(147, 197, 253, 0.2) 100% 18 | ); 19 | } 20 | 21 | /* Dark mode gradient colors */ 22 | :root[data-theme="dark"] .page-gradient { 23 | background: linear-gradient( 24 | 180deg, 25 | var(--sl-color-accent-low) 0%, 26 | rgba(30, 41, 59, 0.8) 25%, 27 | rgba(30, 41, 59, 0.6) 50%, 28 | rgba(30, 41, 59, 0.4) 75%, 29 | rgba(30, 41, 59, 0.2) 100% 30 | ); 31 | } 32 | 33 | .page-gradient { 34 | position: fixed; 35 | top: 0; 36 | left: 0; 37 | right: 0; 38 | bottom: 0; 39 | z-index: -1; 40 | pointer-events: none; 41 | } 42 | 43 | .content-wrapper { 44 | position: relative; 45 | z-index: 1; 46 | } 47 | 48 | /* Light mode card styles */ 49 | :root[data-theme="light"] .custom-card { 50 | background: rgba(255, 255, 255, 0.8); 51 | border: 1px solid rgba(59, 130, 246, 0.2); 52 | } 53 | 54 | :root[data-theme="light"] .custom-card:hover { 55 | background: rgba(255, 255, 255, 0.9); 56 | box-shadow: 0 4px 20px rgba(59, 130, 246, 0.15); 57 | } 58 | 59 | :root[data-theme="light"] .custom-card h3 { 60 | color: var(--sl-color-accent-low); 61 | } 62 | 63 | :root[data-theme="light"] .custom-card p { 64 | color: var(--sl-color-gray-6); 65 | } 66 | 67 | /* Dark mode card styles */ 68 | :root[data-theme="dark"] .custom-card { 69 | background: rgba(30, 41, 59, 0.7); 70 | border: 1px solid rgba(59, 130, 246, 0.2); 71 | } 72 | 73 | :root[data-theme="dark"] .custom-card:hover { 74 | background: rgba(30, 41, 59, 0.8); 75 | box-shadow: 0 4px 20px rgba(59, 130, 246, 0.2); 76 | } 77 | 78 | :root[data-theme="dark"] .custom-card h3 { 79 | color: var(--sl-color-white); 80 | } 81 | 82 | :root[data-theme="dark"] .custom-card p { 83 | color: var(--sl-color-gray-2); 84 | } 85 | 86 | .custom-card { 87 | backdrop-filter: blur(8px); 88 | border-radius: 0.5rem; 89 | padding: 1.5rem; 90 | margin-bottom: 1.5rem; 91 | transition: transform 0.2s, box-shadow 0.2s, background-color 0.2s; 92 | } 93 | 94 | .custom-card:hover { 95 | transform: translateY(-2px); 96 | border-color: rgba(59, 130, 246, 0.4); 97 | } 98 | 99 | .custom-card h3 { 100 | margin-top: 0; 101 | font-size: 1.25rem; 102 | margin-bottom: 0.75rem; 103 | } 104 | 105 | .custom-card p { 106 | margin: 0; 107 | line-height: 1.6; 108 | } 109 | 110 | .hero-section { 111 | text-align: center; 112 | padding: 4rem 2rem; 113 | margin: -1.5rem -1.5rem 2rem -1.5rem; 114 | border-radius: 0.5rem; 115 | } 116 | 117 | /* Light mode hero styles */ 118 | :root[data-theme="light"] .hero-section h1 { 119 | background: linear-gradient( 120 | to right, 121 | var(--sl-color-accent), 122 | rgb(3, 31, 109) 123 | ); 124 | -webkit-background-clip: text; 125 | background-clip: text; 126 | -webkit-text-fill-color: transparent; 127 | } 128 | 129 | :root[data-theme="light"] .hero-section p { 130 | color: var(--sl-color-gray-6); 131 | } 132 | 133 | /* Dark mode hero styles */ 134 | :root[data-theme="dark"] .hero-section h1 { 135 | background: linear-gradient( 136 | to right, 137 | var(--sl-color-accent), 138 | var(--sl-color-accent-high) 139 | ); 140 | -webkit-background-clip: text; 141 | background-clip: text; 142 | -webkit-text-fill-color: transparent; 143 | } 144 | 145 | :root[data-theme="dark"] .hero-section p { 146 | color: var(--sl-color-gray-2); 147 | } 148 | 149 | .hero-section h1 { 150 | font-size: 3.5rem; 151 | font-weight: 700; 152 | margin-bottom: 1rem; 153 | } 154 | 155 | .hero-section p { 156 | font-size: 1.25rem; 157 | max-width: 600px; 158 | margin: 0 auto; 159 | } 160 | 161 | :root[data-theme="light"] { 162 | pre.output { 163 | background-color: rgb(190, 199, 209) !important; 164 | } 165 | } 166 | 167 | /* Dark mode colors. */ 168 | :root { 169 | --sl-color-accent-low: #131e4f; 170 | --sl-color-accent: #3447ff; 171 | --sl-color-accent-high: #b3c7ff; 172 | --sl-color-white: #ffffff; 173 | --sl-color-gray-1: #eceef2; 174 | --sl-color-gray-2: #c0c2c7; 175 | --sl-color-gray-3: #888b96; 176 | --sl-color-gray-4: #545861; 177 | --sl-color-gray-5: #353841; 178 | --sl-color-gray-6: #24272f; 179 | --sl-color-black: #17181c; 180 | } 181 | /* Light mode colors. */ 182 | :root[data-theme='light'] { 183 | --sl-color-accent-low: #c7d6ff; 184 | --sl-color-accent: #364bff; 185 | --sl-color-accent-high: #182775; 186 | --sl-color-white: #17181c; 187 | --sl-color-gray-1: #24272f; 188 | --sl-color-gray-2: #353841; 189 | --sl-color-gray-3: #545861; 190 | --sl-color-gray-4: #888b96; 191 | --sl-color-gray-5: #c0c2c7; 192 | --sl-color-gray-6: #eceef2; 193 | --sl-color-gray-7: #f5f6f8; 194 | --sl-color-black: #ffffff; 195 | } -------------------------------------------------------------------------------- /src/content/docs/background/03 about llvm.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: About LLVM 3 | description: "About LLVM" 4 | sidebar: 5 | order: 3 6 | draft: true 7 | --- 8 | 9 | import {Aside} from "@astrojs/starlight/components"; 10 | 11 | 14 | 15 | 19 | 20 | 21 | `llc` is LLVM static compiler to compile the LLVM source input to assembly language for a specific architecture. This asm file can be then assembled and linked to get the final executable. 22 | 23 | If you already know LLVM, you can jump to the next page. 24 | 25 | import { CardGrid, LinkCard } from "@astrojs/starlight/components"; 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ## An overview on LLVM 34 | 35 | 40 | 41 | The official LLVM website is [llvm.org](https://llvm.org/). LLVM is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name "LLVM" itself is not an acronym; it is the full name of the project. 42 | 43 | LLVM is a powerful and versatile compiler infrastructure project that's become a cornerstone of modern compiler development. it is a collection of modular and reusable compiler and toolchain technologies. Here's a developer-friendly overview: 44 | 45 | ### What LLVM Is 46 | 47 | * **A Framework, Not a Single Compiler:** LLVM provides a set of libraries and tools that you can use to build custom compilers, optimizers, and code generators. It's not a monolithic compiler like GCC. 48 | * **Modular Design:** LLVM's components are designed to be independent and reusable. This allows developers to pick and choose the parts they need for their specific projects. 49 | * **Intermediate Representation (IR):** A key concept is LLVM IR, a low-level, language-independent representation of code. Compilers translate source code into LLVM IR, which is then optimized and transformed before being translated into machine code. This separation of concerns is a major strength of LLVM. 50 | * **Target-Independent and Target-Dependent:** LLVM is designed to be target-independent, meaning the core optimization and analysis passes can be applied to any language and target architecture. Target-specific code generation is handled by backends for different architectures. 51 | * **Extensible:** LLVM is designed to be easily extended with new languages, optimizations, and target architectures. 52 | 53 | ### Key Components 54 | 55 | * **LLVM Core Libraries:** 56 | * **LLVM IR:** The intermediate representation, which is the heart of LLVM's optimization pipeline. 57 | * **Optimizer:** A suite of optimization passes that analyze and transform LLVM IR to improve code performance. 58 | * **Code Generator (Backend):** Translates optimized LLVM IR into machine code for specific target architectures. 59 | * **Clang:** A C, C++, Objective-C, and Objective-C++ compiler that uses LLVM as its backend. It's known for its fast compilation times and excellent error diagnostics. 60 | * **LLDB:** The LLVM debugger, a high-performance debugger. 61 | * **Polly:** A framework for high-level loop and data-locality optimizations. 62 | * **MLIR (Multi-Level Intermediate Representation):** A relatively new sub-project of LLVM that allows for the construction of domain-specific compilers. 63 | 64 | #### Why LLVM is Important 65 | 66 | * **Code Optimization:** LLVM's powerful optimization passes can significantly improve the performance of compiled code. 67 | * **Cross-Platform Development:** LLVM's target-independent design makes it easy to target multiple architectures from a single codebase. 68 | * **Language Development:** LLVM makes it easier to create new programming languages by providing a robust infrastructure for code generation and optimization. 69 | * **Research and Education:** LLVM is widely used in compiler research and education due to its modular design and extensibility. 70 | * **Tooling:** Many useful tools are built on top of LLVM, such as static analyzers and code sanitizers. 71 | 72 | ### Workflow (Simplified) 73 | 74 | 1. **Front End (e.g., Clang):** Parses source code and generates LLVM IR. 75 | 2. **Optimizer:** Applies optimization passes to LLVM IR. 76 | 3. **Backend:** Generates machine code for the target architecture. 77 | 4. **Linker (e.g., LLD):** Combines object files into an executable. 78 | 79 | ### For New Developers 80 | 81 | * Start by understanding LLVM IR. It's the key to understanding how LLVM works. 82 | * Explore Clang to see how a real-world compiler uses LLVM. 83 | * Experiment with simple optimization passes to get a feel for how LLVM's optimization pipeline works. 84 | * Consider using MLIR when building domain specific compilers. 85 | * The LLVM documentation and community are excellent resources for learning more. 86 | 87 | LLVM provides a solid foundation for anyone interested in compiler development. Its modularity, extensibility, and powerful optimization capabilities have made it an essential tool for building modern compilers and related tools. 88 | -------------------------------------------------------------------------------- /src/components/Collapsible.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | type?: 'note' | 'tip' | 'warning' | 'danger'; 5 | defaultOpen?: boolean; 6 | } 7 | 8 | const { title, type = 'note', defaultOpen = false } = Astro.props; 9 | 10 | const icons = { 11 | note: ``, 12 | tip: ``, 13 | warning: ``, 14 | danger: `` 15 | }; 16 | 17 | const colors = { 18 | note: 'details--note', 19 | tip: 'details--tip', 20 | warning: 'details--warning', 21 | danger: 'details--danger' 22 | }; 23 | 24 | const openAttr = defaultOpen ? {open: ''} : {}; 25 | --- 26 | 27 |
28 | 29 | 30 | {title} 31 | 36 | 37 |
38 | 39 |
40 |
41 | 42 | 104 | 105 | -------------------------------------------------------------------------------- /src/components/CodeSnippet.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import fs from "node:fs/promises"; 3 | import { Code as DefaultCode } from "@astrojs/starlight/components"; 4 | 5 | const { id, 6 | beforeContext = 2, 7 | afterContext = 2, 8 | collapse = [], 9 | mark = "unlikely-string", 10 | noContext = false, 11 | noIns = false } = Astro.props; 12 | 13 | import { getEntry } from "astro:content"; 14 | import { readSnippet } from "../util/read-snippet.ts"; 15 | import type { MarkerLineOrRange } from "@expressive-code/plugin-text-markers"; 16 | 17 | const snippet = await getEntry("snippets", id); 18 | if (!snippet) { 19 | throw new Error(`Snippet with id ${id} not found`); 20 | } 21 | const snippetData = await readSnippet(snippet!.data, beforeContext, afterContext); 22 | 23 | // console.log(snippetData); 24 | 25 | const before = snippetData.surrounding.before; 26 | const after = snippetData.surrounding.after; 27 | 28 | let contextStack = snippetData.contextStack; 29 | let hasAfterContext = contextStack.find((c) => c.type === "AFTER"); 30 | ////////////// Context manipulation ///////////////////////// 31 | // if there is no context, make it empty [] 32 | if (contextStack.length === 1) { 33 | const c = contextStack[0] 34 | if (c.type === "NONE") 35 | contextStack = [{text: ">> add at top level in file", type: "NONE"}]; 36 | } 37 | 38 | if (contextStack.length == 0) { 39 | if (before === "") { 40 | contextStack.push({text: ">> new file", type: "FILE"}); 41 | } 42 | } 43 | ////////////// End context manipulation ///////////////////////// 44 | 45 | function stripLeadingComments(content: String) { 46 | return content.split("\n").map((line) => { 47 | if (line.trim().startsWith("// ")) { 48 | return line.slice(3); 49 | } else if(line.trim().startsWith("# ")) { 50 | return line.slice(2); 51 | } 52 | return line; 53 | }).join("\n"); 54 | } 55 | ////////////// Content manipulation //////////////////// 56 | // Construct the actual content with surroundings, remove, and the actual 57 | // content 58 | let removed = null; 59 | let content = snippetData.snippet; 60 | 61 | switch (snippet.data.type) { 62 | case "commented": 63 | case "removed": 64 | // this snippet was removed later, so strip all leading comments 65 | // in the snippet data in snippetData.snippet 66 | content = stripLeadingComments(snippetData.snippet) 67 | break; 68 | case "add": 69 | // do nothing 70 | break; 71 | 72 | case "delete": 73 | removed = stripLeadingComments(content); 74 | content = ""; 75 | break; 76 | default: 77 | if (snippet.data.type.startsWith("replace=")) { 78 | const replaceId = snippet.data.type.split("replace=")[1]; 79 | const replacedSnippet = await getEntry("snippets", replaceId); 80 | if (!replacedSnippet) { 81 | throw new Error(`Snippet with id "${replaceId}" not found`); 82 | } 83 | const replacedSnippetData = await readSnippet(replacedSnippet!.data); 84 | removed = stripLeadingComments(replacedSnippetData.snippet); 85 | } 86 | } 87 | const code = `${before}${removed??""}${content}${after}`; 88 | ////////////// Content manipulation //////////////////// 89 | 90 | const filename = snippetData.filename; 91 | 92 | const lang = filename!.split(".").pop(); 93 | 94 | ////////////// Mark and Ins ///////////////////////// 95 | // Set the mark and ins 96 | const len = code.trim().split("\n").length; 97 | const removeLen = !removed ? 0 98 | : removed.trimEnd().split("\n").length; 99 | const beforeLen = before.trim().length == 0 ? 0 100 | : (before.trimStart().split("\n").length) - 1; // trim end because it ends with a \n 101 | const snipLen = snippet.data.end_lineno - snippet.data.start_lineno - 2; 102 | const snipRangeStart = beforeLen + removeLen + 1; 103 | const snipRangeEnd = snipRangeStart + snipLen; 104 | const markRange = `${snipRangeStart}-${snipRangeEnd}`; 105 | if (id === "deleted-mc-init") { 106 | console.log(markRange) 107 | } 108 | 109 | let finalMark = []; 110 | let finalIns: MarkerLineOrRange[] = []; 111 | let finalDels: MarkerLineOrRange[] = []; 112 | if (removed) { 113 | finalDels = [{ range: `${beforeLen + 1}-${beforeLen + removeLen}` }]; 114 | } 115 | if (noIns || snippet.data.type === "mark") { 116 | // put everything into mark 117 | finalMark =[{ range: markRange }, mark]; 118 | } else if (snippet.data.type === "delete") { 119 | finalIns = []; 120 | } else { 121 | finalMark = mark; 122 | finalIns = [{ range: markRange }]; 123 | } 124 | --- 125 | 126 | 163 |
164 | 165 | { 166 | !noContext && hasAfterContext && 167 |
168 | ↓ after {contextStack[0].text} 169 |
170 | } 171 | 172 | { 173 | !noContext && contextStack.length > 0 && !hasAfterContext && 174 |
175 | {contextStack.map((context, i) => { 176 | let prefix = " ".repeat(i); 177 | let ctype = context.type; 178 | 179 | if (ctype === "NONE"){ 180 | // return 181 | } 182 | 183 | else if (ctype !== "FILE" && i === 0) { 184 | prefix = "→ " 185 | } 186 | else if (i > 0) { 187 | prefix += "↳ "; 188 | } 189 | return ({prefix + context.text}
) 190 | })}
191 | } 192 | 202 |
203 | -------------------------------------------------------------------------------- /docs/pagefind/pagefind-modular-ui.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --pagefind-ui-scale: 0.8; 3 | --pagefind-ui-primary: #034AD8; 4 | --pagefind-ui-fade: #707070; 5 | --pagefind-ui-text: #393939; 6 | --pagefind-ui-background: #ffffff; 7 | --pagefind-ui-border: #eeeeee; 8 | --pagefind-ui-tag: #eeeeee; 9 | --pagefind-ui-border-width: 2px; 10 | --pagefind-ui-border-radius: 8px; 11 | --pagefind-ui-image-border-radius: 8px; 12 | --pagefind-ui-image-box-ratio: 3 / 2; 13 | --pagefind-ui-font: system, -apple-system, ".SFNSText-Regular", 14 | "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", 15 | "Lucida Grande", sans-serif; 16 | } 17 | 18 | [data-pfmod-hidden] { 19 | display: none !important; 20 | } 21 | 22 | [data-pfmod-suppressed] { 23 | opacity: 0 !important; 24 | pointer-events: none !important; 25 | } 26 | 27 | [data-pfmod-sr-hidden] { 28 | -webkit-clip: rect(0 0 0 0) !important; 29 | clip: rect(0 0 0 0) !important; 30 | -webkit-clip-path: inset(100%) !important; 31 | clip-path: inset(100%) !important; 32 | height: 1px !important; 33 | overflow: hidden !important; 34 | overflow: clip !important; 35 | position: absolute !important; 36 | white-space: nowrap !important; 37 | width: 1px !important; 38 | } 39 | 40 | [data-pfmod-loading] { 41 | color: var(--pagefind-ui-text); 42 | background-color: var(--pagefind-ui-text); 43 | border-radius: var(--pagefind-ui-border-radius); 44 | opacity: 0.1; 45 | pointer-events: none; 46 | } 47 | 48 | /* Input */ 49 | 50 | .pagefind-modular-input-wrapper { 51 | position: relative; 52 | } 53 | 54 | .pagefind-modular-input-wrapper::before { 55 | background-color: var(--pagefind-ui-text); 56 | width: calc(18px * var(--pagefind-ui-scale)); 57 | height: calc(18px * var(--pagefind-ui-scale)); 58 | top: calc(23px * var(--pagefind-ui-scale)); 59 | left: calc(20px * var(--pagefind-ui-scale)); 60 | content: ""; 61 | position: absolute; 62 | display: block; 63 | opacity: 0.7; 64 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); 65 | mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); 66 | -webkit-mask-size: 100%; 67 | mask-size: 100%; 68 | z-index: 9; 69 | pointer-events: none; 70 | } 71 | 72 | .pagefind-modular-input { 73 | height: calc(64px * var(--pagefind-ui-scale)); 74 | padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale)); 75 | background-color: var(--pagefind-ui-background); 76 | border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); 77 | border-radius: var(--pagefind-ui-border-radius); 78 | font-size: calc(21px * var(--pagefind-ui-scale)); 79 | position: relative; 80 | appearance: none; 81 | -webkit-appearance: none; 82 | display: flex; 83 | width: 100%; 84 | box-sizing: border-box; 85 | font-weight: 700; 86 | } 87 | 88 | .pagefind-modular-input::placeholder { 89 | opacity: 0.2; 90 | } 91 | 92 | .pagefind-modular-input-clear { 93 | position: absolute; 94 | top: calc(2px * var(--pagefind-ui-scale)); 95 | right: calc(2px * var(--pagefind-ui-scale)); 96 | height: calc(60px * var(--pagefind-ui-scale)); 97 | border-radius: var(--pagefind-ui-border-radius); 98 | padding: 0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale)); 99 | color: var(--pagefind-ui-text); 100 | font-size: calc(14px * var(--pagefind-ui-scale)); 101 | cursor: pointer; 102 | background-color: var(--pagefind-ui-background); 103 | border: none; 104 | appearance: none; 105 | } 106 | 107 | /* ResultList */ 108 | 109 | .pagefind-modular-list-result { 110 | list-style-type: none; 111 | display: flex; 112 | align-items: flex-start; 113 | gap: min(calc(40px * var(--pagefind-ui-scale)), 3%); 114 | padding: calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale)); 115 | border-top: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); 116 | } 117 | 118 | .pagefind-modular-list-result:last-of-type { 119 | border-bottom: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); 120 | } 121 | 122 | .pagefind-modular-list-thumb { 123 | width: min(30%, 124 | calc((30% - (100px * var(--pagefind-ui-scale))) * 100000)); 125 | max-width: calc(120px * var(--pagefind-ui-scale)); 126 | margin-top: calc(10px * var(--pagefind-ui-scale)); 127 | aspect-ratio: var(--pagefind-ui-image-box-ratio); 128 | position: relative; 129 | } 130 | 131 | .pagefind-modular-list-image { 132 | display: block; 133 | position: absolute; 134 | left: 50%; 135 | transform: translateX(-50%); 136 | font-size: 0; 137 | width: auto; 138 | height: auto; 139 | max-width: 100%; 140 | max-height: 100%; 141 | border-radius: var(--pagefind-ui-image-border-radius); 142 | } 143 | 144 | .pagefind-modular-list-inner { 145 | flex: 1; 146 | display: flex; 147 | flex-direction: column; 148 | align-items: flex-start; 149 | margin-top: calc(10px * var(--pagefind-ui-scale)); 150 | } 151 | 152 | .pagefind-modular-list-title { 153 | display: inline-block; 154 | font-weight: 700; 155 | font-size: calc(21px * var(--pagefind-ui-scale)); 156 | margin-top: 0; 157 | margin-bottom: 0; 158 | } 159 | 160 | .pagefind-modular-list-link { 161 | color: var(--pagefind-ui-text); 162 | text-decoration: none; 163 | } 164 | 165 | .pagefind-modular-list-link:hover { 166 | text-decoration: underline; 167 | } 168 | 169 | .pagefind-modular-list-excerpt { 170 | display: inline-block; 171 | font-weight: 400; 172 | font-size: calc(16px * var(--pagefind-ui-scale)); 173 | margin-top: calc(4px * var(--pagefind-ui-scale)); 174 | margin-bottom: 0; 175 | min-width: calc(250px * var(--pagefind-ui-scale)); 176 | } 177 | 178 | /* FilterPills */ 179 | 180 | .pagefind-modular-filter-pills-wrapper { 181 | overflow-x: scroll; 182 | padding: 15px 0; 183 | } 184 | 185 | .pagefind-modular-filter-pills { 186 | display: flex; 187 | gap: 6px; 188 | } 189 | 190 | .pagefind-modular-filter-pill { 191 | display: flex; 192 | justify-content: center; 193 | align-items: center; 194 | border: none; 195 | appearance: none; 196 | padding: 0 calc(24px * var(--pagefind-ui-scale)); 197 | background-color: var(--pagefind-ui-background); 198 | color: var(--pagefind-ui-fade); 199 | border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); 200 | border-radius: calc(25px * var(--pagefind-ui-scale)); 201 | font-size: calc(18px * var(--pagefind-ui-scale)); 202 | height: calc(50px * var(--pagefind-ui-scale)); 203 | cursor: pointer; 204 | white-space: nowrap; 205 | } 206 | 207 | .pagefind-modular-filter-pill:hover { 208 | border-color: var(--pagefind-ui-primary); 209 | } 210 | 211 | .pagefind-modular-filter-pill[aria-pressed="true"] { 212 | border-color: var(--pagefind-ui-primary); 213 | color: var(--pagefind-ui-primary); 214 | } -------------------------------------------------------------------------------- /src/content/docs/section 1/01 triple.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Target Triple 3 | description: "" 4 | sidebar: 5 | order: 2 6 | --- 7 | 8 | import CodeSnippet from "../../../components/CodeSnippet.astro"; 9 | import Details from "../../../components/Collapsible.astro"; 10 | 11 | To generate code for `llc`, we should be able to invoke it as follows: 12 | 13 | ```bash "-mtriple=mipsnova" 14 | llc -mtriple=mipsnova test.ll -o test.s 15 | ``` 16 | 17 | We will register our target as `mipsnova` in LLVM for this to work. 18 | 19 | ## Architectures and SubArchitectures 20 | 21 | Sub-architectures of an architecture are used to represent different versions of 22 | the same architecture. For example, `mips` has the sub architecture `mipsr6` which 23 | corresponds to the MIPS Release 6 architecture. Release 6 differs from previous releases 24 | wherein it adds new and removes few instructions. 25 | 26 |
27 | See the list of SubArchitectures 28 | 29 |
30 | 31 | 32 | import {Aside, Card} from "@astrojs/starlight/components"; 33 | 34 | 39 | 40 | ## Triples 41 | The canonical name for a system type has the form `cpu-vendor-os`. 42 | This is the target triple. 43 | This comes from the `autoconf` tool, which can make decisions based on the 44 | system type. 45 | 46 |
47 | Example: `i686-pc-linux-gnu` 48 | 49 | Breaking down, 50 | 51 | - `i686` is the CPU architecture, which is a 32-bit Intel x86. 52 | - `pc-linux` is the vendor, which is a generic PC. 53 | - `gnu` is the operating system, which is GNU/Linux. 54 | 55 |
56 | 57 |
58 | Find your system configuration name 59 | To find your system configuration name, run this command: 60 | 61 | ```bash withOutput title="llvm-project" 62 | > bash llvm/cmake/config.guess 63 | 64 | x86_64-unknown-linux-gnu 65 | ``` 66 | 67 | To explore further, you can look into this 68 | snippet from the `config.guess` file: 69 | ```bash withOutput title="from config.guess" 70 | > UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown 71 | > UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown 72 | > UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown 73 | > UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown 74 | > echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 75 | 76 | x86_64:Linux:5.15.153.1-microsoft-standard-WSL2:#1 SMP Fri Mar 29 23:14:13 UTC 2024 77 | ``` 78 |
79 | 80 | The `Triple` class parses the architecture, sub-architecture, vendor, os and environment 81 | fields from the string passed in the command line (or in the LLVM module). 82 | 83 | ## Registering our target triple 84 | The CPU architecture type for MipsNova will be `mipsnova`. Recall that this is 85 | a 32-bit architecture. 86 | 87 | ### Triple in `llc` 88 | Let's start by looking at the `llc` driver. 89 | 90 | ```cpp title="llvm/tools/llc/llc.cpp" {6-9} mark="Triple::normalize" 91 | static cl::opt 92 | TargetTriple("mtriple", cl::desc("Override target triple for module")); 93 | ... 94 | 95 | static int compileModule(...) { 96 | if (!TargetTriple.empty()) 97 | IRTargetTriple = Triple::normalize(TargetTriple); 98 | TheTriple = Triple(IRTargetTriple); 99 | } 100 | ``` 101 | 102 | We see that the `normalize` method is called on the Triple object. 103 | This rearranges the triple to its canonical form (`cpu-vendor-os`) by parsing a `-` 104 | separated string that may have the fields in a different order. 105 | 106 | 107 | To add our arch name `mipsnova` to the list of known architectures, we need to 108 | modify a few methods in `Triple`. 109 | 110 |
111 | More info: Parsing the triple string 112 | The constructor for `Triple` takes a string and parses it with these functions. 113 | Right now we are interested in the `parseArch` method. 114 | 115 |
116 | 117 | {/* Let's test this function in a standalone tool. Create a new directory at */} 118 | {/* `llvm/tools/ex` and add the following code: */} 119 | 120 | {/* */} 121 | 122 | For that, we add `mipsnova` to the `ArchType` enum. 123 | 124 | 125 | 126 | Next, we have to add the enum to string conversion for parsing and printing. 127 | 128 | 129 | 130 | 131 | 132 | {/* We also have to add the default object file format our architecture uses. 133 | 134 |
135 | Object file formats 136 | There is a nice */} 137 | 138 | Since `mipsnova` is a 32-bit architecture, we mention that in `getArchPointerBitWidth`. 139 | 140 | 141 | 142 | `mipsnova` is already 32 bit, so add it after `mipsel` in `get32BitArchVariant`. 143 | 144 | 145 | 146 | 147 | 148 |
149 | I am not sure myself. It seems like this is used for the LLVM JIT engine but if you 150 | know more, please let me know (the GitHub repo url is at the top-right of this page!). 151 |
152 | 153 | 154 | ### Setting up the target in CMake 155 | 156 | The target files live in the `llvm/lib/Target/` directory. 157 | We add our directory `Nova` here and add our target 158 | the list of all targets in `llvm/CMakeLists.txt`. 159 | 160 | 161 | Create the `CMakeLists.txt` file in our `Nova` directory. 162 | 163 | 164 | {/*
165 | `LINK_COMPONENTS` does not appear to be required. Removing that section still compiles 166 | `llc` with our target. Figure out why that is so. 167 |
*/} 168 | 169 | Finish by adding our source file `MipsNovaTargetMachine.cpp` to the same directory. 170 | We will get back to this later to fill in the required methods. Right now, this will 171 | enable us to compile `llc` with `mipsnova`. 172 | 173 | 174 | ## Build LLVM with our target 175 | Now, build LLVM with the `Nova` target. 176 | 177 | ```bash Build LLVM 178 | cd llvm-project 179 | 180 | cmake -S llvm -B build -G Ninja \ 181 | -DCMAKE_BUILD_TYPE=Release \ 182 | -DLLVM_BUILD_TESTS=ON \ 183 | -DLLVM_PARALLEL_LINK_JOBS=8 \ 184 | -DLLVM_TARGETS_TO_BUILD='X86;Nova' 185 | 186 | cmake --build build --target llc 187 | ``` 188 | 189 | Now we can see our target listed in the `llc` help output like so: 190 | ```bash withOutput "mipsnova" 191 | > build/bin/llc -version 192 | ... 193 | mipsel - MIPS (32-bit little endian) 194 | mipsnova - MipsNova (32-bit big endian) 195 | x86 - 32-bit X86: Pentium-Pro and above 196 | ... 197 | ``` 198 | 199 |
200 | You can try running the following: 201 | ```bash "-mtriple=mipsnova" withOutput 202 | > build/bin/llc -mtriple=mipsnova < /dev/null 203 | 204 | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. 205 | Stack dump: 206 | 0. Program arguments: build/bin/llc -mtriple=mipsnova 207 | #0 0x000056117674c440 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (build/bin/llc+0x1dce440) 208 | ... 209 | ``` 210 |
211 | The target machine for `mipsnova` is not implemented yet. We will get to that in the next section. 212 | 213 | Before registering the target, we would get this error: 214 | ```log wrap 215 | llc: error: unable to get target for 'mipsnova', see --version and --triple. 216 | ``` 217 |
218 |
219 | Compiling won't work yet because we haven't implemented the target machine for `mipsnova`, 220 | but `llc` now accepts our target triple! 221 | 222 | {/* Our target's name is `NovaMips` instead of `MipsNova` because of a weird error I suspect because of the common prefix with the existing `Mips` target. 223 |
224 | What is the error? 225 | ``` 226 | -- Clang version: 21.0.0git 227 | CMake Error at cmake/modules/LLVM-Config.cmake:271 (message): 228 | Library 'Mips' is a direct reference to a target library for an omitted 229 | target. 230 | Call Stack (most recent call first): 231 | cmake/modules/AddLLVM.cmake:779 (llvm_map_components_to_libnames) 232 | cmake/modules/AddLLVM.cmake:944 (llvm_add_library) 233 | tools/llvm-exegesis/lib/Mips/CMakeLists.txt:16 (add_llvm_library) 234 | ``` 235 |
*/} 236 | 237 | {/* */} -------------------------------------------------------------------------------- /src/util/read-snippet.ts: -------------------------------------------------------------------------------- 1 | import { z } from "astro:content"; 2 | import { join } from "node:path"; 3 | import fs from "node:fs/promises" 4 | 5 | const LLVM_ROOT_DIR = import.meta.env.LLVM_ROOT_DIR; 6 | const SURROUNDING_CONTEXT_LINES = 2; 7 | const LLVM_ROOT_DIR_NAME = "llvm-project" 8 | 9 | /// This is the snippet type in snippets.json 10 | export const snippetType = z.object({ 11 | id: z.string(), 12 | start_lineno: z.number(), 13 | end_lineno: z.number(), 14 | filename: z.string(), 15 | context_stack: z.array(z.object({ 16 | line: z.number(), 17 | type: z.string(), 18 | })), 19 | //type has values "add","replace" 20 | type: z.string(), 21 | }) 22 | 23 | interface Range { 24 | /// The line number to start this range from 25 | /// (inclusive, 1-indexed) 26 | start: number; 27 | /// Number of lines to read (including the 28 | /// start line) 29 | /// Needs to be minimum one 30 | numLines: number; 31 | } 32 | 33 | async function readLines(filename: string, ranges: Range[]) { 34 | const text = await fs.readFile(filename, "utf-8"); 35 | // create array the same length of lines 36 | const lineTexts: string[] = new Array(ranges.length); 37 | // Subtract one from each start since it starts from 1 38 | // and we count from 0 39 | ranges.forEach(range => range.start = range.start - 1) 40 | for (let i = 0; i < text.length; i++) { 41 | 42 | } 43 | } 44 | 45 | interface SnippetContent { 46 | snippet: string; // the main content 47 | contextStack: { // stack of contexts 48 | text: string; // the main context content 49 | type: string; 50 | }[]; 51 | filename: string, // The absolute file path 52 | surrounding: { 53 | lines: number; 54 | before: string; 55 | after: string; 56 | } 57 | } 58 | 59 | interface Context { 60 | text: string; 61 | type: string; 62 | } 63 | 64 | function preProcessContexts(contexts: Context[]): string[] { 65 | return contexts.map((c, i) => { 66 | // if (c.type == "FUNCTION") { 67 | // const fnName = c.text.split("(")[0].split(" ").at(-1); 68 | // return "func " + fnName + " {"; 69 | // } 70 | // max characters should be 71 | let finalContext = ""; 72 | if (i === 0) { 73 | finalContext = "→ "; 74 | } 75 | return finalContext + c.text.trim(); 76 | }) 77 | } 78 | 79 | /** 80 | * Convert relative path to absolute path for files in the 81 | * LLVM_ROOT_DIR directory 82 | * @param relativePath The relative path of the file 83 | */ 84 | function getAbsoluteFilepath(relativePath: string) { 85 | // do this platform-independently 86 | return join(LLVM_ROOT_DIR, relativePath); 87 | } 88 | 89 | const suffixMarkerStart = ['//', '#', ';'] 90 | /** 91 | * Whether this line is a snippet marker comment 92 | * @param trimmedLine The line of code, trimmed 93 | */ 94 | function isSnippetMarkerLine(trimmedLine: string) { 95 | const line = trimmedLine; 96 | for (const startMarker of suffixMarkerStart) { 97 | for (const identifier of ["@s", "-"]) { 98 | if (line.startsWith(startMarker + identifier)) { 99 | return true; 100 | } 101 | } 102 | } 103 | return false; 104 | } 105 | 106 | /** 107 | * Reads the snippet from the source file. This does not follow any references 108 | * to other snippets (like in replace=other-snip-id) 109 | * @param snippet The snippet from snippets.json 110 | * @returns SnippetContent object with the snip content, context 111 | */ 112 | export async function readSnippet( 113 | snippet: z.infer, 114 | beforeContext: number = 2, 115 | afterContext: number = 2): Promise { 116 | const text = await fs.readFile(getAbsoluteFilepath(snippet.filename), "utf-8"); 117 | const lines = text.split("\n"); 118 | let startSnipI = snippet.start_lineno - 1; 119 | let endSnipI = snippet.end_lineno - 1; 120 | let resSnipetText = lines.slice(startSnipI + 1, endSnipI).join("\n") + '\n'; 121 | let resContextText = "a\nb\nc\n"; 122 | const getBeforeContext = (startI: number, 123 | incrementDirection: number = -1, 124 | contextLength: number = SURROUNDING_CONTEXT_LINES 125 | ) => { 126 | let res = []; 127 | let taken = 0; 128 | for (let i = startI; 129 | taken < contextLength && i >= 0 && i < lines.length; 130 | i += incrementDirection 131 | ) { 132 | let line = lines[i]; 133 | let trimmed = line.trimStart() 134 | if (trimmed.startsWith("//@s") || trimmed.startsWith("//-")) { 135 | // this is a snippet part, so skip this line 136 | continue; 137 | } else if (isSnippetMarkerLine(trimmed)) { 138 | continue; 139 | } else { 140 | res.push(line + "\n"); 141 | taken++; 142 | } 143 | } 144 | if (incrementDirection === -1) { 145 | res.reverse(); 146 | } 147 | res[res.length - 1] = res.at(-1)?.slice(0) ?? res.at(-1); 148 | return res.join(""); 149 | } 150 | let surrounding = { 151 | before: getBeforeContext(startSnipI - 1, -1, beforeContext), 152 | after: getBeforeContext(endSnipI + 1, 1, (snippet.type === "end") ? 0 : afterContext), 153 | } 154 | 155 | let contextStack = []; 156 | for (const context of snippet.context_stack) { 157 | if (context.type === "NONE") continue; 158 | contextStack.push({ 159 | text: lines[context.line - 1].trim(), 160 | type: context.type 161 | }) 162 | } 163 | return { 164 | snippet: resSnipetText, 165 | contextStack, 166 | filename: snippet.filename, 167 | surrounding: { 168 | lines: SURROUNDING_CONTEXT_LINES, 169 | before: surrounding.before, 170 | after: surrounding.after, 171 | } 172 | } 173 | } 174 | 175 | export async function readSnippet1(snippet: z.infer): Promise { 176 | const text = await fs.readFile(snippet.filename, "utf-8"); 177 | let context_start = snippet.context_stack.at(-1)?.line ?? -1; 178 | let contextStarts: number[] = []; 179 | const contextTexts: string[] = []; 180 | for (const context of snippet.context_stack) { 181 | contextStarts.push(context.line - 1); 182 | contextTexts.push(""); 183 | } 184 | 185 | context_start--; // so that when we subtract 1 on a newline, 186 | // 0 means we are at the context_start line. 187 | let start = snippet.start_lineno - 1; 188 | let end = snippet.end_lineno - 1; 189 | let snippetText = ""; 190 | let contextText = ""; 191 | let surrounding = { 192 | before: "", 193 | after: "", 194 | }; 195 | for (let i = 0; i < text.length; i++) { 196 | for (let ci = 0; ci < contextStarts.length; ci++) { 197 | if (contextStarts[ci] === 0) { 198 | contextTexts[ci] += text[i]; 199 | } 200 | } 201 | if (context_start === 0) { 202 | // we are in the context start line 203 | // context_start will become -1 after \n is hit 204 | contextText += text[i]; 205 | } 206 | // get the starting surrounding context 207 | if (start <= SURROUNDING_CONTEXT_LINES && start > 0) { 208 | // this corresponds to the lines above the 209 | // snippet start line 210 | surrounding.before += text[i]; 211 | } 212 | if (start < 0) { 213 | // we are in the snippet 214 | if (text[i] === "\n") { 215 | end--; 216 | } 217 | if (end > 0) { 218 | // we are in the snippet 219 | snippetText += text[i]; 220 | } else if (end >= -SURROUNDING_CONTEXT_LINES && end < 0) { 221 | // this corresponds to the lines after the 222 | // snippet end line 223 | surrounding.after += text[i]; 224 | } 225 | } 226 | else if (text[i] === "\n") { 227 | start--; 228 | end--; 229 | context_start--; 230 | for (let i = 0; i < contextStarts.length; i++) { 231 | contextStarts[i]--; 232 | } 233 | } 234 | } 235 | // console.log("All contexts are: ", contextTexts); 236 | const filename = getRelativeFilename(snippet); 237 | return { 238 | snippet: snippetText + "\n", 239 | // context: preProcessContexts(contextTexts.map((text, i) => ({ 240 | // text, 241 | // type: snippet.context_stack[i].type 242 | // }))), 243 | contextStack: contextTexts.map((text, i) => ({ 244 | text: text.trim(), 245 | type: snippet.context_stack[i].type, 246 | })), 247 | filename, 248 | surrounding: { 249 | lines: SURROUNDING_CONTEXT_LINES, 250 | before: surrounding.before, 251 | after: surrounding.after, 252 | } 253 | }; 254 | } 255 | 256 | function getRelativeFilename(snippet: { id: string; start_lineno: number; end_lineno: number; filename: string; context_stack: { type: string; line: number; }[]; type: string; }) { 257 | return snippet.filename.substring(snippet.filename.indexOf(LLVM_ROOT_DIR_NAME) 258 | + LLVM_ROOT_DIR_NAME.length 259 | + 1 260 | ); 261 | } 262 | 263 | -------------------------------------------------------------------------------- /src/content/docs/section 1/02 registers.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Adding Registers" 3 | description: Adding registers for the Nova architecture. 4 | sidebar: 5 | order: 3 6 | --- 7 | import CodeSnippet from "../../../components/CodeSnippet.astro"; 8 | import Details from "../../../components/Collapsible.astro"; 9 | import {LinkCard} from "@astrojs/starlight/components"; 10 | 11 | We now have our triple registered with `llc`. To compile a program, we need 12 | to add our target details so that `llc` knows how to generate code. 13 | 14 | This chapter will add registers to the Nova backend. 15 | 16 | 19 | 20 | ## Registers 21 | The CPU has 32 registers (that are 32-bit). The general-purpose registers have the names `$0` to `$31` and also have aliases. 22 | 23 | We add all these registers in the `NovaRegisterInfo.td` file. 24 | 25 | ### The `Register` class 26 | First, create the top level `Nova.td` file. This pulls in all other 27 | tablegen files. 28 | 29 | 30 | 31 | We have to enumerate our registers as records of the class `Register` defined in `llvm/include/llvm/Target/Target.td`. 32 | 33 | The fields of interest to us are: 34 | - `string Namespace` - the target namespace (Nova in our case) 35 | - `string AsmName` - the assembly name of the register ($zero, $1 etc.) 36 | - `list AltNames` - aliases for the register 37 | - `list SubRegs` - subregisters of the register 38 | - `bit isConstant` - whether the register holds a constant value 39 | 40 | 41 | With this, we can see the tablgen records for the registers. 42 | 43 | ```bash title="Viewing the tablegen output" withOutput 44 | > cd llvm/lib/Target/Nova 45 | > ../../../../build/bin/llvm-tblgen Nova.td -I=../../../include -print-records | grep Nova 46 | 47 | def A0 { // Register NovaGPRReg 48 | string Namespace = "Nova"; 49 | string AsmName = "4"; 50 | list AltNames = []; 51 | list Aliases = []; 52 | list SubRegs = []; 53 | ``` 54 | 55 | Next, we need to group these registers into register classes. 56 | ## Register classes (RC) 57 | {/* https://groups.google.com/g/llvm-dev/c/PZEoHQNCXIs/m/2Rr1W3rYAgAJ?pli=1 */} 58 | A register class is a set of registers that can be used interchangeably in an instruction. For example, the `GPR` register class contains all the general-purpose registers. 59 | 60 | - All virtual registers have a register class assigned. 61 | 62 | - The register allocator also uses the RC to allocate physical registers. 63 | 64 | - A new register class needs to be created for defining register constraints. 65 | 66 | Our general purpose registers will go into the `GPR32` register 67 | class which is defined as a tablgen record deriving from the 68 | `RegisterClass` class. 69 | 70 | 71 | 72 | `"Nova"` is the namespace for the generated definitions by tablgen. 73 | 74 | Next, we mention the type of values these registers can hold. 75 | For our case, these are all 32-bit integers. 76 | 77 | 78 | 79 | The alignment of the register is set to 4 bytes. 80 | 81 | 82 | 83 | Now we list all the registers in this class. 84 | 85 | 86 | 87 | ### Other Register classes 88 | Since we need constraints on allocation (`$zero` should always be an operand 0), 89 | we need another register class for it. 90 | 91 | 92 | 93 | For the rest, we need another class. We can be clever here and 94 | use the set difference operator instead of listing all of them again. 95 | 96 | 97 | 98 | #### Unallocatable registers 99 | Special registers like `$ra` are not to be allocatd by the register allocator to virtual registers. 100 | 101 | 102 | 103 | ## Generating the `.inc` file 104 | 105 | import {Icon} from "@astrojs/starlight/components"; 106 | 107 | 🚀 We have our basic register definitions in place! 108 | 109 | To generate the enum values for our registers, we need to instruct cmake to invoke 110 | `llvm-tblgen` on our `Nova.td` file. 111 | 112 | ### TableGen output 113 | To generate our register file into, we use the `-gen-register-info` TableGen backend. 114 | 115 | 116 | 117 | Let's see what we get from this backend. Run `llvm-tblgen` with our top level td file 118 | with the backend enabled. 119 | ```bash title="Register info from td" 120 | cd llvm/lib/Target/Nova 121 | ../../../../build/bin/llvm-tblgen Nova.td -I=../../../include -gen-register-info > reg.h 122 | 123 | ``` 124 | 125 | ⏭️ We'll take a look at `reg.h` to see what gets generated. 126 | 127 | ## LLVM Classes 128 | We see that there is a class named `MCRegisterClass` that is being forward declared. This class is defined in `llvm/include/llvm/MC/MCRegisterInfo.h`. Let's work our way through this file and related files to understand the register classes in LLVM. 129 | 130 | All these classes are in the `MC` layer. The `MC` layer is the Machine Code layer in LLVM. It is responsible for generating machine code for the target architecture and is used to for the lowest level of code generation. 131 | 132 | ### Representing Registers 133 | Registers are represented as `unsigned` integers in LLVM. Have a look 134 | at the small `MCRegister.h` file. 135 | 136 | - `MCPhysReg` is a physical register number. 137 | - `MCRegUnit` is a register unit number. We'll see this later. 138 | - `MCRegister` wraps an unsigned integer as a physical register. 139 | 140 | The unsigned values are divided into ranges to designate different types of registers: 141 | 142 | - 0 stands for no register used. (Not-A-Register) 143 | - [1;2^30) are all physical registers (assigned by TableGen). 144 | - [2^31;2^32) are virtual registers. 145 | 146 |
147 | Show source 148 | 149 |
150 | 151 | #### Register Class 152 | The `MCRegisterClass` denotes the register class and mirrors the `RegisterClass` in the `Target.td` file. 153 | 154 | #### Register Description 155 | The `MCRegisterDesc` class is used to describe the register. 156 | It contains the Name, SubRegs and SuperRegs along with other details. 157 | 158 | ### `MCRegisterInfo` 159 | A static array of `MCRegisterDesc` is used to define all registers in the target. 160 | This class tracks a pointer to that array. 161 | 162 | Most of this class is boilerplate code to access the register descriptions, 163 | used by the TableGen backend. Some methods in this class are: 164 | 165 | ```cpp 166 | /// This method should return the register where the return 167 | /// address can be found. 168 | MCRegister getRARegister() const; 169 | 170 | /// Return the register which is the program counter. 171 | MCRegister getProgramCounter() const; 172 | 173 | /// Return the number of registers this target has (useful for 174 | /// sizing arrays holding per register information) 175 | unsigned getNumRegs() const; 176 | ``` 177 | 178 | ### The generated file 179 | We can now look at `reg.h`, the generated file. 180 | 181 | Enums are behind the `GET_REGINFO_*` directive in the generated file. 182 | 183 | We have the following things generated for us by tablegen: 184 | 185 | 1. The register enums `Nova::AT, Nova::RA, Nova::PC` etc. 186 | 2. Register class enums `Nova::GPR32RegClassID, Nova::CPURARegRegClassID` etc. 187 | 3. Under `GET_REGINFO_MC_DESC`, we have the `InitNovaMCRegisterInfo` function that initializes the `MCRegisterInfo` 188 | instance for our backend. (these are the definitions, so need to go in a cpp file) 189 | 4. In `GET_REGINFO_HEADER`, The `NovaGenRegisterInfo` class definition, and 190 | 5. Register classes `TargetRegisterClass GPR32RegisterClass` and others. 191 | 6. In `GET_REGINFO_TARGET_DESC`, some functions like `getRegUnitWeight(unsigned RegUnit)` and the constructor 192 | for `NovaGenRegisterInfo`. 193 | 7. Other functions like `getFrameLowering()`: returns a `NovaFrameLowering` object. 194 | 195 | ### Add to cmake 196 | Tell cmake to generate the register info file. 197 | 198 | 199 | ## Adding the NovaRegisterInfo class 200 | 201 | First, add the `NovaGenRegisterInfo` class declaration. 202 | 203 | 204 | Remember to add this to CMakelists.txt as well. 205 | 206 | Add the `NovaRegisterInfo` class. 207 | 208 | 209 | 210 | Revisit this later when we start to lower call and return instructions. 211 | 212 | Now we register our RegisterInfo class with our target. 213 | 214 | First, remove stand-in method we added earlier and move 215 | it to a new file `NovaMCTargetDesc.cpp`. This file contains 216 | all the target description classes for registers 217 | instructions. 218 | 219 | 220 | 221 | Register the Nova's `MCRegisterInfo` class instance in its corresponding 222 | implementation file. 223 | 224 | 225 | And include the file in our build. 226 | 227 | 228 | Move the target getters to a new file so we can reuse them. 229 | 230 | 231 | 232 | Fix our definition written earlier: 233 | 234 | 235 | 236 | Since the `NovaGenRegisterInfo` (which drives from `TargetRegisterInfo`) refers to `NovaFrameLowering`, 237 | we need to add that class. 238 | 239 | 240 | With this, you can successfully compile llc for the `Nova` target. 241 | 242 | The `build` folder should have the tablegen'erated file for the register info. 243 | 244 | ```bash withOutput 245 | > cmake --build build --target llc 246 | > find build -name "NovaGenRegisterInfo.inc" 247 | 248 | build/lib/Target/Nova/NovaGenRegisterInfo.inc 249 | ``` 250 | 251 | Let's move on to add the instructions for Nova. 252 | -------------------------------------------------------------------------------- /src/content/docs/llvm-cg-ref/Selection DAG/01 selection dag.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Instruction selection with Selection DAG 3 | description: This describes the instruction selection process in the LLVM backend using Selection DAG. 4 | --- 5 | import CodeSnippet from "../../../../components/CodeSnippet.astro"; 6 | import Details from "../../../../components/Collapsible.astro"; 7 | import { LinkCard, CardGrid, Aside } from "@astrojs/starlight/components"; 8 | 9 | Once the frontend generates the LLVM IR, the compiler must convert it into machine instructions. 10 | 11 | We will take a look at how LLVM uses a tree pattern-matching algorithm for this and how a backend like 12 | Nova can configure LLVM to generate machine instructions. 13 | 14 | ## Instruction Selection on DAGs 15 | Instruction selection is difficult because a processor provides different ways to perform the same operation. 16 | If we had exactly one way to perform a certain operation, the compiler would simply replace each IR line with the corresponding machine instruction. 17 | 18 | But rarely are things so simple. A simple reg to reg move instruction can be performed with addition, division, subtraction, multiplication, or a bitwise operation (with the other operand being the corresponding identity element). 19 | 20 | LLVM uses a tree pattern-matching structure called a Selection DAG to do this job. 21 | 22 | ### A simple example 23 | A Selection DAG is a directed acyclic graph (DAG) where each node represents an operation or a value. 24 | In particular, the leaves of the DAG are constants or registers (virtual or physical) and other nodes represent operations 25 | that return their result (which can be multiple). 26 | 27 | The selection process works on one basic block at a time. So every basic block is converted into a 28 | separate DAG, with no edges to the other basic blocks. 29 | 30 | Branch operation nodes take a basic block operand as an input, and the basic block is represented as a node in the DAG. 31 | 32 | import { Steps } from "@astrojs/starlight/components"; 33 | 34 | Let's take this simple example. 35 | 36 |
    37 |
  1. 38 | The compiler takes this code as input: 39 | ```c 40 | int main(int a, int b) { 41 | if (a + b == 0) { 42 | return a - b; 43 | } 44 | return 0; 45 | } 46 | ``` 47 |
  2. 48 |
  3. 49 | The LLVM IR for this code is this (consider no optimizations): 50 | 51 | ```c 52 | define i32 @main(i32 %a, i32 %b) { 53 | entry: 54 | %add = add i32 %a, %b 55 | %cmp = icmp eq i32 %add, 0 56 | br i1 %cmp, label %true, label %false 57 | true: 58 | %sub = sub i32 %a, %b 59 | ret i32 %sub 60 | false: 61 | ret i32 0 62 | } 63 | ``` 64 |
  4. 65 |
  5. 66 | import SelDagImage from "../../../../assets/sel-dag-1.svg"; 67 | import { Image } from "astro:assets"; 68 | 69 | This is now converted into a DAG like so: 70 | Selection DAG 71 | 72 | Notice that there are two disconnected components in this DAG. The unconditional branch node 73 | `br` does not use any value from other nodes, so there is nothing to connect it to. 74 | 75 | > Other basic blocks are not shown here. 76 | 77 |
  6. 78 |
  7. 79 | After constructing the DAG, we need to lower it to machine instructions. 80 | In this example, the brcond and setcc is combined into a single branch with compare instruction. 81 | 82 | import SelectedDAGImg from "../../../../assets/sel-dag-2.svg"; 83 | 84 | Selection DAG 85 |
  8. 86 |
87 |
88 | 89 | This completes the lowering phase of the selection. From here we schedule the instructions into a 90 | linear sequence. 91 | 92 | ## SelectionDAG in LLVM 93 | You might notice a problem with the above DAG. The unconditional branch is not connected 94 | to the other nodes, so there is no way to know when to schedule it. (we certainly should not emit it before all other instructions!) 95 | 96 | To represent control flow dependencies like this, nodes return a special type of value called a 97 | chain. Further, if two nodes should be scheduled adjacent to each other, they use a Glue value. 98 | 99 | > Chains represent a dependency between nodes that can't be represented by a data dependency. For example a load following a store that might alias with the address of the load. The store must happen before the load. So the load's chain input is dependent on the store's chain output either directly or through other intermediate nodes that also have chain inputs and outputs. There can be multiple chains in parallel in the DAG. TokenFactor nodes are used to merge separate chains. The InstrEmitter ensures that the chain dependency is satisfied when emitting the linear instruction sequence after isel. But nothing guarantees that parallel chains won't be interleaved. After a node is schedule all of the nodes dependent on it either through data or chain are checked to see if they are now ready to schedule. The scheduler will pick from the ready to schedule nodes without any concern for whether they were on the same chain as the last node scheduled. 100 | > 101 | > Glue is stricter, it says that two nodes must be scheduled adjacent to each other in the linear instruction sequence. 102 | 103 | 107 | 108 | 132 | #### Viewing the selection DAG output 133 | While compiling with `llc`, you can use these options to view the DAG output: 134 | 135 | - `-view-dag-combine1-dags` displays the DAG after being built, before the first optimization pass. 136 | - `-view-legalize-dags` displays the DAG before Legalization. 137 | - `-view-dag-combine2-dags` displays the DAG before the second optimization pass. 138 | - `-view-isel-dags` displays the DAG before the Select phase. 139 | - `-view-sched-dags` displays the DAG before Scheduling. 140 | 141 | 146 | 147 | ## Selection stages 148 | Read about the stages of selection in the LLVM documentation and the post linked below. 149 | 150 | 155 | 156 | 159 | 160 | ## Selection DAG in LLVM 161 | 162 | 172 | 173 | ## Classes used 174 | Let's look at the classes in LLVM that represent the selection DAG. 175 | 176 | ### MVT and EVT 177 | These two represent the types in LLVM. 178 | #### 1. MVT 179 | This is a union of all types that targets support in Selection DAG. 180 |
181 | Machine Value Type. Every type that is supported natively by some 182 | processor targeted by LLVM occurs here. This means that any legal value 183 | type can be represented by an MVT. 184 |
185 | 186 | It is basically a single `uint16_t` value that is used to represent the type. 187 | We can see all types listed in `ValueTypes.td`. 188 | We can query the properties of the type using the `MVT` class methods. 189 | 190 | The types included in `MVT` are integers like i1, i32, floating point types like f32, bf16, 191 | vectors like v1i1, v1f16 and others like `Glue` and `Other`. 192 | Some of the types included in this are pasted here for reference: 193 | 194 | 195 | 196 | #### 2. EVT 197 | This extends the types include in the `MVT` set with types that the LLVM IR supports, like `i3` or `<4 x i5>` 198 | but may not be supported directly by a target. 199 | 200 |
201 | Extended Value Type. Capable of holding value types which are not native 202 | for any processor (such as the i12345 type), as well as the types an MVT 203 | can represent. 204 |
205 | 206 | An EVT object holds an `MVT` value and an LLVM `Type` object. 207 | 208 | We can initialize an `EVT` object in two ways shown below. Note that they can be implicitly casted from `MVT`. 209 | 210 | {/* The two fields start off invalid for the default constructor. */} 211 | 212 | We then have a number of methods to query the type. Some of them are: 213 | 1. `isSimple()` - Returns true if the type is a simple type (i.e. not a vector or complex type). 214 | Here "extended" means that this is not an MVT type but an LLVMTy. 215 | 2. `isInteger()` - Returns true if the type is an integer type. 216 | and so on. 217 | 218 |
219 | Run `llc` with `-debug-only=isel` to see the selection DAG. 220 | 221 | ```cpp 222 | Initial selection DAG: %bb.1 'main:true' 223 | SelectionDAG has 8 nodes: 224 | t0: ch,glue = EntryToken 225 | t2: i32,ch = CopyFromReg t0, Register:i32 %3 226 | t4: i32,ch = CopyFromReg t0, Register:i32 %4 227 | t5: i32 = sub t2, t4 228 | t7: ch = CopyToReg t0, Register:i32 %1, t5 229 | ``` 230 |
231 | 232 | ### SDNode 233 | These are the nodes in the selection DAG. 234 | 235 | Each node has an opcode, some flags, a list of operands and a list of result values. 236 | 237 | #### Opcodes 238 | Opcodes are represented by an int32_t value, but is taken from the `ISD` enum or 239 | a target-specific `XXXISD` enum. 240 | 241 | We can query for the SDNode opcode with these methods: 242 | 243 | 244 | You can see all of the built-in opcodes in `ISDOpcodes.h`. 245 | 246 | 247 | {/* The main class is `SelectionDAG`. */} 248 | {/* */} -------------------------------------------------------------------------------- /docs/pagefind/pagefind-ui.css: -------------------------------------------------------------------------------- 1 | .pagefind-ui__result.svelte-j9e30.svelte-j9e30{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-j9e30.svelte-j9e30:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-thumb.svelte-j9e30.svelte-j9e30{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-j9e30.svelte-j9e30{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-j9e30.svelte-j9e30{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-j9e30.svelte-j9e30{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-j9e30.svelte-j9e30{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-j9e30.svelte-j9e30{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf{display:flex;flex-direction:column;padding-left:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf:first-of-type{padding-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{font-size:.9em;position:relative}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:before{content:"\2937 ";position:absolute;top:0;right:calc(100% + .1em)}.pagefind-ui__result-thumb.svelte-4xnkmf.svelte-4xnkmf{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-4xnkmf.svelte-4xnkmf{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-4xnkmf.svelte-4xnkmf{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-4xnkmf.svelte-4xnkmf{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-4xnkmf.svelte-4xnkmf{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}legend.svelte-1v2r7ls.svelte-1v2r7ls{position:absolute;clip:rect(0 0 0 0)}.pagefind-ui__filter-panel.svelte-1v2r7ls.svelte-1v2r7ls{min-width:min(calc(260px * var(--pagefind-ui-scale)),100%);flex:1;display:flex;flex-direction:column;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{border:0;padding:0}.pagefind-ui__filter-block.svelte-1v2r7ls.svelte-1v2r7ls{padding:0;display:block;border-bottom:solid calc(2px * var(--pagefind-ui-scale)) var(--pagefind-ui-border);padding:calc(20px * var(--pagefind-ui-scale)) 0}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls{font-size:calc(16px * var(--pagefind-ui-scale));position:relative;display:flex;align-items:center;list-style:none;font-weight:700;cursor:pointer;height:calc(24px * var(--pagefind-ui-scale))}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls::-webkit-details-marker{display:none}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls:after{position:absolute;content:"";right:calc(6px * var(--pagefind-ui-scale));top:50%;width:calc(8px * var(--pagefind-ui-scale));height:calc(8px * var(--pagefind-ui-scale));border:solid calc(2px * var(--pagefind-ui-scale)) currentColor;border-right:0;border-top:0;transform:translateY(-70%) rotate(-45deg)}.pagefind-ui__filter-block[open].svelte-1v2r7ls .pagefind-ui__filter-name.svelte-1v2r7ls:after{transform:translateY(-70%) rotate(-225deg)}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{display:flex;flex-direction:column;gap:calc(20px * var(--pagefind-ui-scale));padding-top:calc(30px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls{position:relative;display:flex;align-items:center;gap:calc(8px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls:before{position:absolute;content:"";top:50%;left:calc(8px * var(--pagefind-ui-scale));width:0px;height:0px;border:solid 1px #fff;opacity:0;transform:translate(calc(4.5px * var(--pagefind-ui-scale) * -1),calc(.8px * var(--pagefind-ui-scale))) skew(-5deg) rotate(-45deg);transform-origin:top left;border-top:0;border-right:0;pointer-events:none}.pagefind-ui__filter-value.pagefind-ui__filter-value--checked.svelte-1v2r7ls.svelte-1v2r7ls:before{opacity:1;width:calc(9px * var(--pagefind-ui-scale));height:calc(4px * var(--pagefind-ui-scale));transition:width .1s ease-out .1s,height .1s ease-in}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls{margin:0;width:calc(16px * var(--pagefind-ui-scale));height:calc(16px * var(--pagefind-ui-scale));border:solid 1px var(--pagefind-ui-border);appearance:none;-webkit-appearance:none;border-radius:calc(var(--pagefind-ui-border-radius) / 2);background-color:var(--pagefind-ui-background);cursor:pointer}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls:checked{background-color:var(--pagefind-ui-primary);border:solid 1px var(--pagefind-ui-primary)}.pagefind-ui__filter-label.svelte-1v2r7ls.svelte-1v2r7ls{cursor:pointer;font-size:calc(16px * var(--pagefind-ui-scale));font-weight:400}.pagefind-ui--reset *:where(:not(html,iframe,canvas,img,svg,video):not(svg *,symbol *)){all:unset;display:revert;outline:revert}.pagefind-ui--reset *,.pagefind-ui--reset *:before,.pagefind-ui--reset *:after{box-sizing:border-box}.pagefind-ui--reset a,.pagefind-ui--reset button{cursor:revert}.pagefind-ui--reset ol,.pagefind-ui--reset ul,.pagefind-ui--reset menu{list-style:none}.pagefind-ui--reset img{max-width:100%}.pagefind-ui--reset table{border-collapse:collapse}.pagefind-ui--reset input,.pagefind-ui--reset textarea{-webkit-user-select:auto}.pagefind-ui--reset textarea{white-space:revert}.pagefind-ui--reset meter{-webkit-appearance:revert;appearance:revert}.pagefind-ui--reset ::placeholder{color:unset}.pagefind-ui--reset :where([hidden]){display:none}.pagefind-ui--reset :where([contenteditable]:not([contenteditable="false"])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}.pagefind-ui--reset :where([draggable="true"]){-webkit-user-drag:element}.pagefind-ui--reset mark{all:revert}:root{--pagefind-ui-scale:.8;--pagefind-ui-primary:#393939;--pagefind-ui-text:#393939;--pagefind-ui-background:#ffffff;--pagefind-ui-border:#eeeeee;--pagefind-ui-tag:#eeeeee;--pagefind-ui-border-width:2px;--pagefind-ui-border-radius:8px;--pagefind-ui-image-border-radius:8px;--pagefind-ui-image-box-ratio:3 / 2;--pagefind-ui-font:system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif}.pagefind-ui.svelte-e9gkc3{width:100%;color:var(--pagefind-ui-text);font-family:var(--pagefind-ui-font)}.pagefind-ui__hidden.svelte-e9gkc3{display:none!important}.pagefind-ui__suppressed.svelte-e9gkc3{opacity:0;pointer-events:none}.pagefind-ui__form.svelte-e9gkc3{position:relative}.pagefind-ui__form.svelte-e9gkc3:before{background-color:var(--pagefind-ui-text);width:calc(18px * var(--pagefind-ui-scale));height:calc(18px * var(--pagefind-ui-scale));top:calc(23px * var(--pagefind-ui-scale));left:calc(20px * var(--pagefind-ui-scale));content:"";position:absolute;display:block;opacity:.7;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");-webkit-mask-size:100%;mask-size:100%;z-index:9;pointer-events:none}.pagefind-ui__search-input.svelte-e9gkc3{height:calc(64px * var(--pagefind-ui-scale));padding:0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));background-color:var(--pagefind-ui-background);border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);font-size:calc(21px * var(--pagefind-ui-scale));position:relative;appearance:none;-webkit-appearance:none;display:flex;width:100%;box-sizing:border-box;font-weight:700}.pagefind-ui__search-input.svelte-e9gkc3::placeholder{opacity:.2}.pagefind-ui__search-clear.svelte-e9gkc3{position:absolute;top:calc(3px * var(--pagefind-ui-scale));right:calc(3px * var(--pagefind-ui-scale));height:calc(58px * var(--pagefind-ui-scale));padding:0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));color:var(--pagefind-ui-text);font-size:calc(14px * var(--pagefind-ui-scale));cursor:pointer;background-color:var(--pagefind-ui-background);border-radius:var(--pagefind-ui-border-radius)}.pagefind-ui__drawer.svelte-e9gkc3{gap:calc(60px * var(--pagefind-ui-scale));display:flex;flex-direction:row;flex-wrap:wrap}.pagefind-ui__results-area.svelte-e9gkc3{min-width:min(calc(400px * var(--pagefind-ui-scale)),100%);flex:1000;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__results.svelte-e9gkc3{padding:0}.pagefind-ui__message.svelte-e9gkc3{box-sizing:content-box;font-size:calc(16px * var(--pagefind-ui-scale));height:calc(24px * var(--pagefind-ui-scale));padding:calc(20px * var(--pagefind-ui-scale)) 0;display:flex;align-items:center;font-weight:700;margin-top:0}.pagefind-ui__button.svelte-e9gkc3{margin-top:calc(40px * var(--pagefind-ui-scale));border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);height:calc(48px * var(--pagefind-ui-scale));padding:0 calc(12px * var(--pagefind-ui-scale));font-size:calc(16px * var(--pagefind-ui-scale));color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background);width:100%;text-align:center;font-weight:700;cursor:pointer}.pagefind-ui__button.svelte-e9gkc3:hover{border-color:var(--pagefind-ui-primary);color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background)} 2 | -------------------------------------------------------------------------------- /docs/pagefind/pagefind-modular-ui.js: -------------------------------------------------------------------------------- 1 | (()=>{var b=Object.defineProperty;var w=(i,e)=>{for(var t in e)b(i,t,{get:e[t],enumerable:!0})};var f={};w(f,{FilterPills:()=>h,Input:()=>l,Instance:()=>p,ResultList:()=>a,Summary:()=>o});var r=class i{constructor(e){this.element=document.createElement(e)}id(e){return this.element.id=e,this}class(e){return this.element.classList.add(e),this}attrs(e){for(let[t,s]of Object.entries(e))this.element.setAttribute(t,s);return this}text(e){return this.element.innerText=e,this}html(e){return this.element.innerHTML=e,this}handle(e,t){return this.element.addEventListener(e,t),this}addTo(e){return e instanceof i?e.element.appendChild(this.element):e.appendChild(this.element),this.element}};var T=async(i=100)=>new Promise(e=>setTimeout(e,i)),l=class{constructor(e={}){if(this.inputEl=null,this.clearEl=null,this.instance=null,this.searchID=0,this.debounceTimeoutMs=e.debounceTimeoutMs??300,e.inputElement){if(e.containerElement){console.warn("[Pagefind Input component]: inputElement and containerElement both supplied. Ignoring the container option.");return}this.initExisting(e.inputElement)}else if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Input component]: No selector supplied for containerElement or inputElement");return}this.inputEl.addEventListener("input",async t=>{if(this.instance&&typeof t?.target?.value=="string"){this.updateState(t.target.value);let s=++this.searchID;if(await T(this.debounceTimeoutMs),s!==this.searchID)return null;this.instance?.triggerSearch(t.target.value)}}),this.inputEl.addEventListener("keydown",t=>{t.key==="Escape"&&(++this.searchID,this.inputEl.value="",this.instance?.triggerSearch(""),this.updateState("")),t.key==="Enter"&&t.preventDefault()}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad()})}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No container found for ${e} selector`);return}if(t.tagName==="INPUT")console.warn(`[Pagefind Input component]: Encountered input element for ${e} when a container was expected`),console.warn("[Pagefind Input component]: Treating containerElement option as inputElement and proceeding"),this.initExisting(e);else{t.innerHTML="";let s=0;for(;document.querySelector(`#pfmod-input-${s}`);)s+=1;let n=new r("form").class("pagefind-modular-input-wrapper").attrs({role:"search","aria-label":"Search this site",action:"javascript:void(0);"});new r("label").attrs({for:`pfmod-input-${s}`,"data-pfmod-sr-hidden":"true"}).text("Search this site").addTo(n),this.inputEl=new r("input").id(`pfmod-input-${s}`).class("pagefind-modular-input").attrs({autocapitalize:"none",enterkeyhint:"search"}).addTo(n),this.clearEl=new r("button").class("pagefind-modular-input-clear").attrs({"data-pfmod-suppressed":"true"}).text("Clear").handle("click",()=>{this.inputEl.value="",this.instance.triggerSearch(""),this.updateState("")}).addTo(n),n.addTo(t)}}initExisting(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No input element found for ${e} selector`);return}if(t.tagName!=="INPUT"){console.error(`[Pagefind Input component]: Expected ${e} to be an element`);return}this.inputEl=t}updateState(e){this.clearEl&&(e&&e?.length?this.clearEl.removeAttribute("data-pfmod-suppressed"):this.clearEl.setAttribute("data-pfmod-suppressed","true"))}register(e){this.instance=e,this.instance.on("search",(t,s)=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.updateState(t))})}focus(){this.inputEl&&this.inputEl.focus()}};var g=i=>{if(i instanceof Element)return[i];if(Array.isArray(i)&&i.every(e=>e instanceof Element))return i;if(typeof i=="string"||i instanceof String){let e=document.createElement("div");return e.innerHTML=i,[...e.childNodes]}else return console.error(`[Pagefind ResultList component]: Expected template function to return an HTML element or string, got ${typeof i}`),[]},v=()=>{let i=(e=30)=>". ".repeat(Math.floor(10+Math.random()*e));return`
  • 2 |
    3 |
    4 |

    ${i(30)}

    5 |

    ${i(40)}

    6 |
    7 |
  • `},y=i=>{let e=new r("li").class("pagefind-modular-list-result"),t=new r("div").class("pagefind-modular-list-thumb").addTo(e);i?.meta?.image&&new r("img").class("pagefind-modular-list-image").attrs({src:i.meta.image,alt:i.meta.image_alt||i.meta.title}).addTo(t);let s=new r("div").class("pagefind-modular-list-inner").addTo(e),n=new r("p").class("pagefind-modular-list-title").addTo(s);return new r("a").class("pagefind-modular-list-link").text(i.meta?.title).attrs({href:i.meta?.url||i.url}).addTo(n),new r("p").class("pagefind-modular-list-excerpt").html(i.excerpt).addTo(s),e.element},E=i=>{if(!(i instanceof HTMLElement))return null;let e=window.getComputedStyle(i).overflowY;return e!=="visible"&&e!=="hidden"?i:E(i.parentNode)},d=class{constructor(e={}){this.rawResult=e.result,this.placeholderNodes=e.placeholderNodes,this.resultFn=e.resultFn,this.intersectionEl=e.intersectionEl,this.result=null,this.waitForIntersection()}waitForIntersection(){if(!this.placeholderNodes?.length)return;let e={root:this.intersectionEl,rootMargin:"0px",threshold:.01};new IntersectionObserver((s,n)=>{this.result===null&&s?.[0]?.isIntersecting&&(this.load(),n.disconnect())},e).observe(this.placeholderNodes[0])}async load(){if(!this.placeholderNodes?.length)return;this.result=await this.rawResult.data();let e=this.resultFn(this.result),t=g(e);for(;this.placeholderNodes.length>1;)this.placeholderNodes.pop().remove();this.placeholderNodes[0].replaceWith(...t)}},a=class{constructor(e){if(this.intersectionEl=document.body,this.containerEl=null,this.results=[],this.placeholderTemplate=e.placeholderTemplate??v,this.resultTemplate=e.resultTemplate??y,e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind ResultList component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind ResultList component]: No container found for ${e} selector`);return}this.containerEl=t}append(e){for(let t of e)this.containerEl.appendChild(t)}register(e){e.on("results",t=>{this.containerEl&&(this.containerEl.innerHTML="",this.intersectionEl=E(this.containerEl),this.results=t.results.map(s=>{let n=g(this.placeholderTemplate());return this.append(n),new d({result:s,placeholderNodes:n,resultFn:this.resultTemplate,intersectionEl:this.intersectionEl})}))}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerHTML="")})}};var o=class{constructor(e={}){if(this.containerEl=null,this.defaultMessage=e.defaultMessage??"",this.term="",e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Summary component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Summary component]: No container found for ${e} selector`);return}this.containerEl=t,this.containerEl.innerText=this.defaultMessage}register(e){e.on("search",(t,s)=>{this.term=t}),e.on("results",t=>{if(!this.containerEl||!t)return;if(!this.term){this.containerEl.innerText=this.defaultMessage;return}let s=t?.results?.length??0;this.containerEl.innerText=`${s} result${s===1?"":"s"} for ${this.term}`}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerText=`Searching for ${this.term}...`)})}};var h=class{constructor(e={}){if(this.instance=null,this.wrapper=null,this.pillContainer=null,this.available={},this.selected=["All"],this.total=0,this.filterMemo="",this.filter=e.filter,this.ordering=e.ordering??null,this.alwaysShow=e.alwaysShow??!1,this.selectMultiple=e.selectMultiple??!1,!this.filter?.length){console.error("[Pagefind FilterPills component]: No filter option supplied, nothing to display");return}if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind FilterPills component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind FilterPills component]: No container found for ${e} selector`);return}t.innerHTML="";let s=`pagefind_modular_filter_pills_${this.filter}`,n=new r("div").class("pagefind-modular-filter-pills-wrapper").attrs({role:"group","aria-labelledby":s});this.alwaysShow||n.attrs({"data-pfmod-hidden":!0}),new r("div").id(s).class("pagefind-modular-filter-pills-label").attrs({"data-pfmod-sr-hidden":!0}).text(`Filter results by ${this.filter}`).addTo(n),this.pillContainer=new r("div").class("pagefind-modular-filter-pills").addTo(n),this.wrapper=n.addTo(t)}update(){let e=this.available.map(t=>t[0]).join("~");e==this.filterMemo?this.updateExisting():(this.renderNew(),this.filterMemo=e)}pushFilters(){let e=this.selected.filter(t=>t!=="All");this.instance.triggerFilter(this.filter,e)}pillInner(e,t){return this.total?`${e} (${t})`:`${e}`}renderNew(){this.available.forEach(([e,t])=>{new r("button").class("pagefind-modular-filter-pill").html(this.pillInner(e,t)).attrs({"aria-pressed":this.selected.includes(e),type:"button"}).handle("click",()=>{e==="All"?this.selected=["All"]:this.selected.includes(e)?this.selected=this.selected.filter(s=>s!==e):this.selectMultiple?this.selected.push(e):this.selected=[e],this.selected?.length?this.selected?.length>1&&(this.selected=this.selected.filter(s=>s!=="All")):this.selected=["All"],this.update(),this.pushFilters()}).addTo(this.pillContainer)})}updateExisting(){let e=[...this.pillContainer.childNodes];this.available.forEach(([t,s],n)=>{e[n].innerHTML=this.pillInner(t,s),e[n].setAttribute("aria-pressed",this.selected.includes(t))})}register(e){this.instance=e,this.instance.on("filters",t=>{if(!this.pillContainer)return;this.selectMultiple?t=t.available:t=t.total;let s=t[this.filter];if(!s){console.warn(`[Pagefind FilterPills component]: No possible values found for the ${this.filter} filter`);return}this.available=Object.entries(s),Array.isArray(this.ordering)?this.available.sort((n,c)=>{let m=this.ordering.indexOf(n[0]),_=this.ordering.indexOf(c[0]);return(m===-1?1/0:m)-(_===-1?1/0:_)}):this.available.sort((n,c)=>n[0].localeCompare(c[0])),this.available.unshift(["All",this.total]),this.update()}),e.on("results",t=>{this.pillContainer&&(this.total=t?.unfilteredResultCount||0,this.available?.[0]?.[0]==="All"&&(this.available[0][1]=this.total),this.total||this.alwaysShow?this.wrapper.removeAttribute("data-pfmod-hidden"):this.wrapper.setAttribute("data-pfmod-hidden","true"),this.update())})}};var P=async(i=50)=>await new Promise(e=>setTimeout(e,i)),u;try{document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"&&(u=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?modular-ui.js.*$/)[1])}catch{u="/pagefind/"}var p=class{constructor(e={}){this.__pagefind__=null,this.__initializing__=null,this.__searchID__=0,this.__hooks__={search:[],filters:[],loading:[],results:[]},this.components=[],this.searchTerm="",this.searchFilters={},this.searchResult={},this.availableFilters=null,this.totalFilters=null,this.options={bundlePath:e.bundlePath??u,mergeIndex:e.mergeIndex??[]},delete e.bundlePath,delete e.resetStyles,delete e.processResult,delete e.processTerm,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations,this.pagefindOptions=e}add(e){e?.register?.(this),this.components.push(e)}on(e,t){if(!this.__hooks__[e]){let s=Object.keys(this.__hooks__).join(", ");console.error(`[Pagefind Composable]: Unknown event type ${e}. Supported events: [${s}]`);return}if(typeof t!="function"){console.error(`[Pagefind Composable]: Expected callback to be a function, received ${typeof t}`);return}this.__hooks__[e].push(t)}triggerLoad(){this.__load__()}triggerSearch(e){this.searchTerm=e,this.__dispatch__("search",e,this.searchFilters),this.__search__(e,this.searchFilters)}triggerSearchWithFilters(e,t){this.searchTerm=e,this.searchFilters=t,this.__dispatch__("search",e,t),this.__search__(e,t)}triggerFilters(e){this.searchFilters=e,this.__dispatch__("search",this.searchTerm,e),this.__search__(this.searchTerm,e)}triggerFilter(e,t){this.searchFilters=this.searchFilters||{},this.searchFilters[e]=t,this.__dispatch__("search",this.searchTerm,this.searchFilters),this.__search__(this.searchTerm,this.searchFilters)}__dispatch__(e,...t){this.__hooks__[e]?.forEach(s=>s?.(...t))}async __clear__(){this.__dispatch__("results",{results:[],unfilteredTotalCount:0}),this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}async __search__(e,t){this.__dispatch__("loading"),await this.__load__();let s=++this.__searchID__;if(!e||!e.length)return this.__clear__();let n=await this.__pagefind__.search(e,{filters:t});n&&this.__searchID__===s&&(n.filters&&Object.keys(n.filters)?.length&&(this.availableFilters=n.filters,this.totalFilters=n.totalFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})),this.searchResult=n,this.__dispatch__("results",this.searchResult))}async __load__(){if(this.__initializing__){for(;!this.__pagefind__;)await P(50);return}if(this.__initializing__=!0,!this.__pagefind__){let e;try{e=await import(`${this.options.bundlePath}pagefind.js`)}catch(t){console.error(t),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindComposable Instance"].join(` 8 | `)),document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?console.error(`[DEBUG: Loaded from ${document.currentScript?.src??"bad script location"}]`):console.error("no known script location")}await e.options(this.pagefindOptions||{});for(let t of this.options.mergeIndex){if(!t.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let s=t.bundlePath;delete t.bundlePath,await e.mergeIndex(s,t)}this.__pagefind__=e}this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}};window.PagefindModularUI=f;})(); 9 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 404 | LLVM to MIPS 28 | Skip to content

    404

    Page not found. Check the URL or try using the search bar.
    --------------------------------------------------------------------------------