Hsin Hung Lin
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Optimizing Brainfuck JIT Compiler contributed by <`f5120125`>, <`diana0651`> <`andy19950`>, <`TotallyWrong`>,<`cjTsai3030`> --- [GitHub連結](https://github.com/TotallyWrong/bf-runtime) >> Git repository 的名稱不該包含 "group",而是明確程式碼產出的簡稱,例如 `bf-jitc` 之類的名稱 [name=jserv] >> 名稱已修改 [name=TotallyWrong] >> 原本的程式碼太亂,難以維護,乾脆重新從 [bf-runtime](https://github.com/sysprog21/bf-runtime) fork 並且移植 DynASM 程式碼進來 [name=jserv] >> 已經做完repository變更,將會盡速把剩下的Code移過去。 [name=TotallyWrong] >> 很久沒看到程式碼進展,趕快跟我約時間討論 [name=jserv] --- ## 影片連結 1. [BrainF*ck 介紹及討論](https://youtu.be/1U-F7DJLG7Q) --- 林鑫宏 2. [基本編譯器架構介紹](https://youtu.be/k7RqLBU12tE) --- 蔡承哲 3. [Jit-BrainF*ck 編譯器介紹以及 IR 的優化]() --- 老師說我們先不用拍影片>< 4. [C to BrainF*ck 編譯器介紹]( https://youtu.be/ml9xsnxe0Gg) --- 吳庭安 --- ## 暫時溝通區塊 Group Communication >>有任何問題或需要溝通的事請留在這區塊,內容在完成後會刪除。 1. f5120125, diana0651 我們似乎應該建立個 IR層請參考IR Section。 --- ## Brainfuck compiler 共筆整理 ### 預期目標 - 整理同學們的共筆,讓大家看這份文件就能了解基礎編譯器的執行過程 >> 關於共筆整理,可參考 [Brainfuck JIT Compiler in Rust](https://dontpanic.cn/brainfuck-jit-compiler-in-rust/) 文章的架構 [name=jserv] - 研究 Brainfuck 的指令,轉成 IR 後使其最佳化 --- >> >> 還有些極端的特例,如 [movfuscator](https://github.com/xoreaxeaxeax/movfuscator), [walking drives](http://www.catb.org/jargon/html/W/walking-drives.html), [trapcc](https://github.com/jbangert/trapcc) [name=jserv] - 實做出最基本的 C to BF 編譯器 --- >> 可從設計 macro-language 開始,參考 [afbc](https://github.com/fcostin/abfc) - 最佳化我們的編譯器 >> 注意: 我們需要設計 dynamic compiler (或 virtual machine),而非 static optimizing compiler [name=jserv] ### 專案任務 >>把要做的事情轉換成小任務在完成後請勾選,如果任務不夠細請自行修改。 --- - [ ]整理同學們的共筆。 - [ ]建立C轉IR的Macro。 - [ ]建立BF轉IR層。 - [ ]IR優化Routine。 - [ ]BF JIT Compiler 所做出的程式 Cache優化。 - [ ]BF JIT Compiler 編譯器本身的記憶體行為。 - [ ]共同影片錄製。 --- ### 內容 1. BrainF*ck 介紹 2. Compiler 介紹 3. JIT 介紹 4. 簡單介紹 BrainF*ck 編譯器設計與IR層優化 5. C to BrainF*ck --- ### 1. BrainF*ck 介紹 - [Brainf*ck wikipedia](https://en.wikipedia.org/wiki/Brainfuck) Brain F\*ck 是一個由 Urban Müller 在 1993年所開發的[深奧的程式語言](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%A5%A5%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80)而這個語言具備圖靈完備性,整個語言只有八道指令。 - 符合 Turing complete - 一個萬能圖靈機:可以執行任何計算 - 具有無限存儲能力的通用程式語言 - Brainfuck 的基本模型是由一個**連續的記憶體位置**以及一個**指向記憶體的指標**所組成 --- - - 八個指令,其中兩個是 I/O 動作 - 對記憶體單元作直接的存取 - 透過 data pointer 在 data cell 中進行不同的指令 --- ![](https://hackpad-attachments.s3.amazonaws.com/mimihalo.hackpad.com_fF2g9dyypfz_p.478178_1445700611617_undefined) --- - 每個字元都是一個 token,如果 token不是這8個指令的符號之一就會背捨棄掉。 - 前進和後退跳躍運算元需要是完整的一對才是正確的指令 - 八個運算字元 - **`>` 和`<`**: 存在 **stack buffer overrun**可能性因為程式沒有進行記憶體邊界檢查. - **`+` and `-`**: 把記憶體位置的值加一或減一 - **`.` 和 `,`**: 提供輸入和輸出功能 - 把指標所指的記憶體位置的值stdout以 ASCII code 輸出 - 以stdin 讀取一個byte以ASCII的值寫入指標所指的記憶體位置。 - **`[` and `]`**: 回圈結構 --- 在操作BF的code時有很多難處需要克服。 1.BF只可以一次操作一個byte並沒有支援Int或這類比較大型態,計算的Overflow 需要克服。 2.BF一次指可以操做一個指標,所以再做值的運算時需要自行紀錄所使用參數的個數和記憶體位置。 3.BF來執行Branch或Flow Control都很不直覺因為程式中都只能對指標做操作並沒有指令跳躍的功能。 --- ### 2. 編譯器架構基本介紹 - [參考投影片](http://www.slideshare.net/jserv/how-a-compiler-works-gnu-toolchain) 以及 [影片](https://www.youtube.com/watch?v=RYXFLxnLsiM) 電腦上所執行的程式都是用某種程式語言編寫的,但在程式可以執行之前它首先需要翻譯成能夠被電腦執行的型式,而完成這項翻譯工作的軟體系統稱為編譯器(Compiler)-龍書 --- --- 編譯器的3個主要目標: 1. 產生正確的程式碼。 2. 編譯器應有效提高許多輸入程式的效能。 3. 需要降低編譯時間,以支援快速的開發和除錯周期。 --- #### 2.1 編譯器流程圖 一般編譯器的架構可以簡單拆成一個或數個 frontends ,以及一個或數個 backends Frontend 先將原始碼轉換為一個或多個的 IR ,方便進行最佳化,再由 Backend 將 IR 轉換成執行程式。 --- [Intermediate Representation](https://en.wikipedia.org/wiki/Intermediate_representation) ```flow st=>start: Source code e=>end: Target code op=>operation: Frontend op2=>operation: Optimizer (IR) op3=>operation: Backend st->op->op2->op3->e ``` --- #### 2.2 Frontend (Parser) - 主要做程式碼的字串切割,語意語法的偵錯 - BF 的編譯器只須逐個字讀入就好,但在實做 C to BF 的編譯器就需要 Parser 的幫助,我們會使用老師的 [AMaCC](https://github.com/jserv/amacc) 來改寫。 >> AMaCC 裡頭的技巧比較多,不容易懂,可對照其他專案: >> * [c2bf](https://github.com/arthaud/c2bf) >> * [Brainfuck code generation](https://esolangs.org/wiki/Brainfuck_code_generation) >> 其中 Macro systems 很值得一看,裡頭不乏精巧的設計 [name=jserv] 1. **Lexical Analyzer**:將原始碼切成 token stream 2. **Syntax Analyzer**:將 token 帶入定義好的 identifier 最後轉成 sytax tree 3. **Semantic Analyzer**:判斷 syntax tree 是否符合 regular expression 以及檢查每個節點的型態是否合法 ( type checking ) --- ### 2.3 IR (Intermediate Representation) - 原本對於不同的 Frontend / Backend 都需要實做一個編譯器,也就是 C 對 arm, mips, x86_64 要三個編譯器,而 Java 也需要對應的另外三個編譯器 - 後來引入中間表示 (IR),只須實做一個編譯器就能夠把不同的 Frontend 對應到 不同的 Backend,達到支援各式前後端的功能 >> Reference: [bfc: An industrial-grade brainfuck compiler](https://github.com/Wilfred/bfc) [name=jserv] - 重點是可以針對 IR 來做最佳化,讓產生的組合語言能夠更精簡,排列更有利於執行,或者刪減一些不必要產生的組語 --- ### 2.4 Backend - 對於 GCC 來說 IR 最後會產生 RTL 格式的檔案。 - Register Transfer Language(RTL) 是一種類似 LISP 的表示法 - IR 產生出來的 RTL 檔裡面使用的暫存器是無限多個的,但在現實中暫存器的個數是有限的。 - 所以會需要 Register Allocation 把原本無限多個虛擬暫存器去對應到有限個實體暫存器( ex. eax,ebx ) - 最後將目標的機械碼產生出來 --- ### 3. Just-in-Time Compilter 基本介紹 [Brainfuck JIT Compiler in Rust](https://dontpanic.cn/brainfuck-jit-compiler-in-rust/)與[Just-in-time compilation](https://en.wikipedia.org/wiki/Just-in-time_compilation) ### 4.簡單介紹 BrainF*ck 編譯器設計與IR層優化 #### 4.1 BF 最佳化策略 1. **Contraction**: 連續的 `+,-,>,<` 可以被壓縮 2. **Clear loops**: `[-]` 可以直接把值歸零 3. **Copy loops**: `[->*+*<*]`有出現這種 pattern 的迴圈代表複製 - `*` 代表可以重複出現,但左右shift的次數要相等 4. **Multiplication loops**: `[->+*>*+*<*<]` 跟上面的 pattern 相似但多了一個倍數的乘法 - copy loops 算是 multiplication loops 的其中一種情況 5. **Operation offsets**: pattern 前面如果有連續的位移代表需要執行動作的 offset - 目前沒有實做這個部份,可以參考 [BF最佳化策略](http://calmerthanyouare.org/2015/01/07/optimizing-brainfuck.html) 6. **Scan loops**: 找到左或右第一個值為零的位置 |IR |C | |:--------|:------------------------| |Add(x) |`mem[p] += x;` | |Sub(x) |`mem[p] -= x;` | |Right(x) |`p += x;` | |Left(x) |`p -= x;` | |Out |`putchar(mem[p]);` | |In |`mem[p] = getchar();` | |Open |`while(mem[p]){` | |Add |`}` | |Clear |`mem[p] = 0;` | |Mul(x,y) |`mem[p+x] += mem[p] * y;`| |ScanLeft |`p -= (long)((void *)(mem + p) - memrchr(mem, 0, p+1));` | |ScanRight|`p += (long)(memchr(mem + p, 0, sizeof(mem)) - (void *)(mem + p));` | #### 4.2 優化策略 - The IR code generated by the semantic routines is analyzed and transformed into functionally equivalent but improved IR code. - This phase can be very complex and slow - Peephole optimization --- #### 4.3 進階的效能最佳化 [bf-runtime](https://github.com/sysprog21/bf-runtime) 老師希望我們把既有的程式整合到bf-runtime中,我們從了解bf-runtime這個程式開始:以下是 main 程式的流程圖 ![](https://i.imgur.com/HbD4gG1.png) ### 參考 team4 https://hackmd.io/s/r1pcJ0Dkx https://hackmd.io/s/B1n0oiNxx --- ### 5. C to BrainF*ck - 原始碼參考 [c2bf](https://github.com/arthaud/c2bf) - 使用的語言是 [meta language (ml)](https://en.wikipedia.org/wiki/ML_(programming_language)) - 若有看不懂的語法請參考 [ML notes](http://homepages.inf.ed.ac.uk/stg/NOTES/node102.html#keywords) 搜尋關鍵字查詢使用方法 #### 5.1 Types 定義後面所需要用到的各種名稱 ##### 5.1.1定義型態 - `type` 關鍵字用於定義 - `of` 關鍵字意思為 左邊繼承右邊 - `int, bool, char` 是基本型態 - 第一個字母大寫的都是自己定義的名稱,方便未來使用 ```ml= type var_type = |Int |Bool |Array of var_type type constant = |IntConst of int |BoolConst of bool |CharConst of char ``` --- ##### 5.1.2定義 expression - `expression` 可以是變數,常數或者運算等 - 原始程式碼還有許多定義這邊就舉加法為例 - `Add`這種運算必須是兩種`expression`組合而成 - `*` 關鍵字代表組合而不是乘法喔 ```ml= type expression = |Var of string |Const of constant |Add of expression * expression ``` --- ##### 5.1.3 定義 parameters - 必須要有變數型態以及變數名稱 - `list` 關鍵字代表前面括弧內的變數可能是多種型態的組合 ```ml= type function_parameters = (var_type * string) list ``` --- ##### 5.1.4 定義 statement - 有 Define,Assign,IF,WHILE 等等的組合 - 原程式碼還有許多定義這邊舉幾個比較關鍵的 statement ```ml= type statement = |Define of var_type * string * expression |Assign of string * expression |If of expression * statement list * statement list |While of expression * statement list |For of statement * expression * statement * statement list |Function of (var_type * expression) option * string * function_parameters * statement list (* (returned type, returned expression), name, parameters, instructions *) ``` --- ##### 5.1.5 定義program - `program`就是由許多`statement`組合而成的 - 而`statement`上面有定義過了,就是透過不斷參考上面的定義來達到了解整支程式碼的意義 ```ml= type program = statement list ``` --- ##### 5.1.6 定義 Token - 定義最基本的 digit, identifier - identifier 是用來對應我們宣告的變數名稱,必須由一個英文字開始,後面可以接數個英文字母、數字或底線 ``` digit = ['0'-'9'] ident = ['a'-'z' 'A'-'Z'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* ``` - 定義未來會用到的 token 當作保留字(以大寫T當開頭) ``` rule tokenize = parse | "void" { TVoid } | "int" { TInt } | "bool" { TBool } | '(' { TLeftPar } | ')' { TRightPar } | '{' { TLeftBrace } | '}' { TRightBrace } | '[' { TOpenBracket } | ']' { TCloseBracket } | '+' { TPlus } | '-' { TMinus } | '*' { TMul } | '/' { TDiv } | '^' { TXor } | '<' { TInf } | "<=" { TInfEq } | "==" { TEq } | "!=" { TNotEq } | '>' { TSup } | ">=" { TSupEq } | "&&" { TAnd } | "||" { TOr } | '!' { TNot } | ';' { TSemicolon } | ',' { TComma } | '=' { TAssign } | "if" { TIf } | "else" { TElse } | "while" { TWhile } | "for" { TFor } | "return" { TReturn } | (digit+ as num) { TIntConst(int_of_string num) } | "true" { TBoolConst(true) } | "false" { TBoolConst(false) } ``` --- #### 5.2 Parser 使用上面的定義來宣告 ##### 5.2.1 宣告 Token - 宣告各種關鍵字,也是為了未來使用,看到越後面可以越了解 - 這邊原始程式都用大寫 T 開頭來代表 Token ```ml= %token <string> TVar %token <int> TIntConst %token <bool> TBoolConst %token <char> TCharConst %token <string> TStringConst %token TVoid TInt TBool %token TLeftPar TRightPar TLeftBrace TRightBrace TOpenBracket TCloseBracket %token TPlus TMinus TMul TDiv TXor %token TInf TInfEq TEq TNotEq TSup TSupEq TAnd TOr TNot %token TSemicolon TComma TAssign TReturn %token TIf TElse TWhile TFor %token TReadChar TWriteChar %token TEOF ``` --- ##### 5.2.2 定義語意 (Semantic) - `%start` 代表 syntax tree 的 root node - 而 `nt_program` 可以變成 `nt_statment nt_program` 以此類推完成 syntax tree - `nt_statment` 以及 `nt_expression` 就按照上面定義的內容。 ```ml= %start nt_program nt_program : | TEOF { [] } | nt_statement nt_program { $1::$2 } ; nt_statements : | { [] } | nt_statement nt_statements { $1::$2 } ; nt_function_parameters : | { [] } | nt_type TVar nt_function_parameters_list { ($1, $2)::$3 } ; nt_function_parameters_list : | { [] } | TComma nt_type TVar nt_function_parameters_list { ($2, $3)::$4 } ; nt_function_arguments : | { [] } | nt_expression nt_function_arguments_list { $1::$2 } ; nt_function_arguments_list : | { [] } | TComma nt_expression nt_function_arguments_list { $2::$3 } ; ``` - compiler 會檢查程式碼是否符合定義的 syntax tree 以及檢查語法 (變數型態,重複定義或未定義的變數) - 看不懂的話我們用一張圖來解釋若編譯器讀到其中一行程式碼的情形。![](https://i.imgur.com/fPFH8Kv.jpg) --- #### 5.3 IR 層 - 解決每行 C 程式語法的定義後,接下來要把它對應到 IR - 這邊定義了幾個 IR |IR |Description | |:------------------------|:---------------------------| |`bf_clean(x)` |`Mem[x] = 0` | |`bf_move(src,dst)` |`Mem[dst] = Mem[src]` | |`bf_copy(src,dst,temp)` |`Mem[dst] = Mem[src] * temp`| |`bf_generate(value,pos)` |`Mem[pos] = value` | |`bf_add(x,y)` |`Mem[x] += Mem[y]` | |`bf_sub(x,y)` |`Mem[x] -= Mem[y]` | |`bf_mul(x,y,temp1,temp2)`|`Mem[x] *= Mem[y]` | |`bf_div(x,y,temp1~4)` |`Mem[x] /= Mem[y]` | |`bf_minus(x,temp)` |`Mem[x] = -Mem[x]` | |`bf_eq(x,y)` |`Mem[x] = Mem[x] == Mem[y]` | |`bf_not(x,temp)` |`Mem[x] = !Mem[x]` | |`bf_and(x,y,temp1,temp2)`|`Mem[x] = Mem[x] & Mem[y]` | |`bf_or(x,y,temp)` |`Mem[x] = Mem[x] | Mem[y]` | |`bf_inf_eq(x,y,temp)` |`Mem[x] = Mem[x] <= Mem[y]` | |`bf_array_write(x,y,z)` |`Mem[x[y]] = Mem[z]` | |`bf_array_access(x,y,z)` |`Mem[x] = Mem[y[z]]` | |`bf_xor(x)` |`Mem[x] = Mem[x+1] XOR Mem[x+2]`| --- ##### 5.3.1 BF 基本指令 - 這邊定義 10 種基本指令 - 比原本的多出下列兩種 1. `GOTO(x)` : 比較 x 與現在位置 pos 的距離,使用連續的位移來移動到 x 2. `Debug(x)` : 編譯時有使用 -d (debug模式) 就會把 x 輸出到檔案內 ``` |Goto(x)::q -> if x >= pos then (String.make (x - pos) '>') ^ (aux x q) else (String.make (pos - x) '<') ^ (aux x q) |Debug(x)::q -> x ^ (aux pos q) ``` - 剩下的怕大家看不懂還是列舉一下 1. `Left : '<'` 2. `Right : '>'` 3. `Incr : '+'` 4. `Decr : '-'` 5. `Out : '.'` 6. `In : ','` 7. `OBrac : '['` 8. `CBrac : ']'` --- ##### 5.3.2 介紹完基本指令後就可以來看 IR 的實做了 - `bf_clean` ``` let bf_clean x = [Goto(x); OBrac; Decr; CBrac] ``` - `bf_move` ``` let bf_move src dst = [Goto(src); OBrac; Goto(dst); Incr; Goto(src); Decr; CBrac] ``` - `bf_copy` ``` let bf_copy src dst temp = [ Goto(src); OBrac; Goto(dst); Incr; Goto(temp); Incr; Goto(src); Decr; CBrac; Goto(temp); OBrac; Goto(src); Incr; Goto(temp); Decr; CBrac] ``` --- ## Reference - [Brainfuck JIT Compiler in Rust](https://dontpanic.cn/brainfuck-jit-compiler-in-rust/) - [memrchr 組語](http://osxr.org:8080/glibc/source/sysdeps/x86_64/memrchr.S) - LLVM and Brain F*ck [JservBlog](http://blog.linux.org.tw/~jserv/archives/2011/04/_llvm_brainfuck.html) ###### tags:`f5120125` `diana0651` `andy19950` `TotallyWrong` `cjTsai3030`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully