├── 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 | [](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 |
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 |
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 |
48 |
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 |
65 |
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 |
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 |
78 |
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 |
85 |
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