├── .gitignore ├── CNAME ├── HACKING.md ├── LICENSE ├── LICENSE.MinCaml ├── MinCaml.md ├── README.md ├── bundle.js ├── dune-project ├── examples ├── ack.ml ├── echo.ml ├── fact.ml ├── fib.ml ├── gcd.ml └── metacirc.ml ├── footer.pug ├── framework.js ├── header.pug ├── index.html ├── index.pug ├── joe.opam ├── main.css ├── package.json ├── src ├── arm64 │ ├── dune │ ├── emit.ml │ ├── emit.mli │ ├── libmincaml.S │ ├── libmincaml.c │ ├── regAlloc.ml │ ├── regAlloc.mli │ └── stub.c ├── joe │ ├── alpha.ml │ ├── alpha.mli │ ├── asm.ml │ ├── asm.mli │ ├── assoc.ml │ ├── assoc.mli │ ├── beta.ml │ ├── beta.mli │ ├── closure.ml │ ├── closure.mli │ ├── constFold.ml │ ├── constFold.mli │ ├── dune │ ├── elim.ml │ ├── elim.mli │ ├── id.ml │ ├── inline.ml │ ├── inline.mli │ ├── kNormal.ml │ ├── kNormal.mli │ ├── lexer.mll │ ├── m.ml │ ├── main.ml │ ├── main.mli │ ├── parser.mly │ ├── s.ml │ ├── simm.ml │ ├── simm.mli │ ├── syntax.ml │ ├── type.ml │ ├── typing.ml │ ├── typing.mli │ ├── unparser.ml │ ├── unparser.mli │ ├── util.ml │ ├── virtual.ml │ └── virtual.mli ├── sparc │ └── emit.ml ├── ulc │ └── ulc.ml ├── vm │ ├── config.ml │ ├── dune │ ├── emit.ml │ ├── emit.mli │ ├── float.c │ ├── insts.ml │ ├── insts.mli │ ├── libmincaml.c │ ├── main.ml │ └── vM.ml ├── x64 │ ├── dune │ ├── emit.ml │ ├── emit.mli │ ├── lib.c │ ├── libmincaml.S │ ├── libmincaml.c │ ├── regAlloc.ml │ ├── regAlloc.mli │ └── stub.c └── x86 │ ├── dune │ ├── emit.ml │ ├── emit.mli │ ├── libmincaml.S │ └── libmincaml.c ├── styles ├── _aside.scss ├── _canvas.scss ├── _code.scss ├── _exe.scss ├── _footer.scss ├── _header.scss ├── _index.scss ├── _intro.scss ├── _list.scss ├── _om.scss ├── _resources.scss ├── _semantics.scss ├── _stack.scss ├── _status.scss ├── _types.scss ├── main.scss └── mathjax.js └── vm.opam /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | .vscode 3 | *.s 4 | *.joe 5 | *.exe -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | joe.groupoid.space 2 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | Перелік можливих завдань для виконання 2 | ====================================== 3 | 4 | ВИКОНАНО 01 Зробити по прикладу PRП (Department of Mathematical 5 | and Computing Science, Tokyo Institute of Technology) 6 | свій форк MinCaml — два бінарника vm (віртуальну машину і компілятор) 7 | і joe (компілятор і бінарні бекенди Intel та ARM). 8 | Зменшити кількість опцій, зробити дефаулти і 9 | дослідіти ack.ml на граничні можливі обчислювальні 10 | координати m та n. 11 | 12 | ВИКОНАНО 02 Перекласти 13 | A Crash Course for the MinCaml Compiler. Переклад 14 | з англійської версії автора, яка отримана перекладом з японської. 15 | 16 | ВИКОНАНО 03 Імпорт бекенда для ARM64 (thata/min-caml-aarch64). 17 | В цьому бекенді інструкція проміжного асемблера Set 18 | називається Load Immediate (Li). Також ARM бекенд 19 | не містить операцій множення і ділення (замінені 20 | на виклики функцій runtime). 21 | 22 | ВИКОНАНО 04 Імпорт бекендів для x64 x86 та BAC (prg.is.titech.ac.jp). 23 | x64 бекенд не завершений, BAC бекенд не достатньо перевірений. 24 | x86 бекенд не перевірений. 25 | 26 | ВІДКРИТА 05 Бекенд для RISC-V. 27 | 28 | ВІДКРИТА 06 Не працює байт-код програми метациркулярного 29 | інтерпретатора після вводу першого числа — розміру 30 | байтів в стрічці байт-коду. Дослідити і усунути проблему. 31 | 32 | ВИКОНАНО 07 Порт на OCaml 5.0. 33 | Проблема була у префіксах імен функцій для лінкера в runtime файлах. 34 | 35 | ВІДКРИТА 08 Бекенд для Motorola 68000. 36 | 37 | ВІДКРИТА 09 Бекенд для MIPS. 38 | 39 | ВІДКРИТА 10 Бекенд для PowerPC або POWER. 40 | 41 | ВІДКРИТА 11 Бекенд для SPARC (відновити працездатність оригінального 42 | першого бекенда Ейдзіру Суміі). 43 | 44 | ВІДКРИТА 12 Бекенд для SH4. 45 | 46 | ВІДКРИТА 13 Бекенд для PDP-11. 47 | 48 | ВИКОНАНО 14 Після генерації асемблера в компіляторі joe 49 | Зробити виклик gcc через exec прямо в main модулі. 50 | Звернути увагу, що для різних бекендів можуть бути 51 | різні gcc компілятори і параметри, а для vm бекенда 52 | це взагалі непотрібно. 53 | 54 | ВІДКРИТА 15 Потрібно зменшити футпрінт бінарника joe 55 | з 35 кб до 8-12 кб хоча б (двох-трьох сторінок). 56 | Системне програмування. 57 | 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Groupoid Infinity 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | YOU CANNOT USE THIS SOFTWARE IN ANY (PROVABLE BY MONEY TRACE) 8 | PROCESS CHAIN OF EXTERMINATING UKRAINIANS BY ANY MEANS OF FASCIST 9 | ACTIONS AGAINST OUR TERRITORIAL INTEGRITY, CULTURAL DIVERSITY BY 10 | APPLYING MILITARY INVASIONS, ECONOMICAL WARS, HUMANITARIAN DISASTERS, 11 | ARTFICIAL HOLODOMORS, GENOCIDE, RAPING, LOOTING, ROBBERIES, SPREADING 12 | FAKE INFORMATION, AND OTHER CONTEMPORARY WEAPONS OF WAR AT SCALE 13 | OR IN INVIDIVUAL MANNER. 14 | 15 | YOU CANNOT USE THIS SOFTWARE BY ANY MEANS IN INTEREST OF LEGAL 16 | ENTITIES OR INDIVIDUALS WHO IS SUPPORTING NOW OR WAS SUPPORTING 17 | BACK THEN FASCISM, RUSCISM, COMMUNISM, CHAUVINISM, HUMILIATION, 18 | AND OTHER SUPPRESSIVE IDEOLOGIES IN DIFFERENT EXPRESSIONS. 19 | 20 | STOP KILLING UKRAINIANS, 21 | THE COUNTER RENDERS TENS OF MILLIONS. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 24 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 25 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 26 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 27 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 28 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 29 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 | -------------------------------------------------------------------------------- /LICENSE.MinCaml: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2024 Eijiro Sumii, Moe Masuko, and Kenichi Asai 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 9 | - Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the 12 | distribution. 13 | 14 | - Neither the name of Information-Technology Promotion Agency, the 15 | name of University of Pennsylvania, the name of University of 16 | Tokyo, the name of Tohoku University, the name of Ochanomizu 17 | University, the name of Eijiro Sumii, the name of Moe Masuko, nor 18 | the name of Kenichi Asai may be used to endorse or promote products 19 | derived from this software without specific prior written 20 | permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Joe: MinCaml compiler and virtual machine 2 | ========================================= 3 | 4 | Features 5 | -------- 6 | 7 | * 4K LOC 8 | * Intel/ARM 64-bit native compiler (joe) 9 | * Byte-code IR compiler (joe) 10 | * Byte-code IR virtual machine (vm) 11 | * Original MinCaml codebase 12 | 13 | Setup 14 | ----- 15 | 16 | ``` 17 | $ opam install ppx_deriving ppx_inline_test 18 | $ dune build 19 | ``` 20 | 21 | Samples 22 | ------- 23 | 24 | Compile sample for MinCaml bytecode: 25 | 26 | ```sh 27 | $ _build/install/default/bin/vm -compile examples/fact.ml 28 | ``` 29 | 30 | Run sample from MinCaml bytecode in VM interpreter: 31 | 32 | ```sh 33 | $ _build/install/default/bin/vm -exec examples/fact.joe 34 | 10 35 | 3628800 36 | ``` 37 | 38 | Compile sample for Apple M1 (from MinCaml to Assembler): 39 | 40 | ```sh 41 | $ _build/install/default/bin/joe -arm examples/ack.ml 42 | Generating assembly...OK 43 | ``` 44 | 45 | Compile assembler and link for macOS: 46 | 47 | ```sh 48 | $ gcc examples/ack.arm.s src/arm64/libmincaml.c src/arm64/stub.c -o ack 49 | ``` 50 | 51 | Run sample natively on M1: 52 | 53 | ```sh 54 | $ ./ack 55 | 509 56 | ``` 57 | 58 | Resources 59 | --------- 60 | 61 | * Kenji Nozawa. SICP in Standard ML. 62 | * Eijiro Sumii. A Crash Course for the MinCaml compiler. 63 | * Eijiro Sumii. MinCaml: A Simple and Efficient Compiler for a Minimal Functional Language. 64 | * М. Сохацький. Інформатика. 65 | * C. Okasaki. Purely Functional Data Structures. 66 | * R. Harper. Programming in Standard ML. 67 | * Andrew W. Appel. Modern Compiler Implementation in ML. 68 | * Andreas Rossberg. HaMLet: To Be Or Not To Be Standard ML. 69 | 70 | Credits 71 | ------- 72 | 73 | * Namdak Tonpa 74 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.9) 2 | -------------------------------------------------------------------------------- /examples/ack.ml: -------------------------------------------------------------------------------- 1 | let rec ack m n = 2 | if m = 0 then n + 1 else 3 | if n = 0 then ack (m-1) 1 else ack (m-1) (ack m (n-1)) in 4 | let res = ack 3 3 in 5 | let _ = print_int res in 6 | print_newline() 7 | -------------------------------------------------------------------------------- /examples/echo.ml: -------------------------------------------------------------------------------- 1 | print_string(read_string ()) -------------------------------------------------------------------------------- /examples/fact.ml: -------------------------------------------------------------------------------- 1 | let rec fact x = if x = 0 then 1 else mul x (fact (x-1)) in 2 | let z = read_int() in print_int (fact z);print_newline() 3 | -------------------------------------------------------------------------------- /examples/fib.ml: -------------------------------------------------------------------------------- 1 | let rec fib n = if n <= 1 then 1 else fib(n-1) + fib(n-2) in print_int(fib (read_int()));print_newline() 2 | -------------------------------------------------------------------------------- /examples/gcd.ml: -------------------------------------------------------------------------------- 1 | let rec gcd m n = 2 | if m <= 2 then n else 3 | if m <= n then gcd m (n-m) else 4 | gcd n (m - n) in 5 | print_int (gcd 1230 262728293) 6 | 7 | -------------------------------------------------------------------------------- /examples/metacirc.ml: -------------------------------------------------------------------------------- 1 | let rec is_mj _ = false in 2 | 3 | let rec cast_fAII x = x in 4 | let rec cast_fIAI x = x in 5 | 6 | let rec pop stack sp = stack.(sp - 1) in 7 | let rec push stack sp v = stack.(sp) <- v in 8 | 9 | let rec interp stack sp bytecode pc = 10 | let instr = bytecode.(pc) in 11 | if instr = 0000000 then interp stack sp bytecode (pc+1) 12 | else if instr = 01 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in stack.(sp-2) <- (v1+v2) ; interp stack (sp-1) bytecode (pc+1) 13 | else if instr = 02 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in stack.(sp-2) <- (v1-v2) ; interp stack (sp-1) bytecode (pc+1) 14 | else if instr = 03 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in stack.(sp-2) <- (mul v1 v2) ; interp stack (sp-1) bytecode (pc+1) 15 | else if instr = 04 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in stack.(sp-2) <- (div v1 v2) ; interp stack (sp-1) bytecode (pc+1) 16 | else if instr = 05 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in stack.(sp-2) <- (rem v1 v2) ; interp stack (sp-1) bytecode (pc+1) 17 | else if instr = 06 then let v1 = stack.(sp-1) in let n = (if v1 = 0 then 1 else 0) in stack.(sp-1) <- n; interp stack sp bytecode (pc+1) 18 | else if instr = 07 then let v1 = stack.(sp-1) in stack.(sp-1) <- (-v1); interp stack sp bytecode (pc+1) 19 | else if instr = 08 then let v2 = stack.(sp-1) in let v1 = stack.(sp-2) in let n = (if v1 < v2 then 1 else 0) in stack.(sp-2) <- n ; interp stack (sp-1) bytecode (pc+1) 20 | else if instr = 09 then let v1 = stack.(sp-1) in let v2 = stack.(sp-2) in let v = (if v1 > v2 then 1 else 0) in stack.(sp-2) <- v ; interp stack (sp-1) bytecode (pc+1) 21 | else if instr = 10 then let v1 = stack.(sp-1) in let v2 = stack.(sp-2) in let v = (if v1 = v2 then 1 else 0) in stack.(sp-2) <- v ; interp stack (sp-1) bytecode (pc+1) 22 | else if instr = 11 then stack.(sp-1) 23 | else if instr = 12 then let addr = bytecode.(pc+1) in let v = stack.(sp-1) in let sp2 = sp - 1 in if v = 0 then (interp stack sp2 bytecode addr) else interp stack sp2 bytecode (pc + 2) 24 | else if instr = 13 then let addr = bytecode.(pc+1) in interp stack sp bytecode addr 25 | else if instr = 14 then let addr = bytecode.(pc+1) in let rands = bytecode.(pc+2) in 26 | (stack.(sp) <- 200 ; (* push jit flag *) stack.(sp+1) <- pc+3 ; interp stack (sp+2) bytecode addr) 27 | else if instr = 16 then let n = bytecode.(pc+1) in let v = stack.(sp-n-1) in stack.(sp) <- v ; interp stack (sp+1) bytecode (pc+2) 28 | else if instr = 17 then let v = stack.(sp-1) in stack.(sp) <- v ; interp stack (sp+1) bytecode (pc+1) 29 | else if instr = 18 then let v = stack.(sp-1) in let _ = stack.(sp-2) in stack.(sp-2) <- v ; interp stack (sp-1) bytecode (pc+1) 30 | else if instr = 19 then let _ = stack.(sp-1) in interp stack (sp-1) bytecode (pc+1) 31 | else if instr = 20 then (stack.(sp) <- 0 ; interp stack (sp+1) bytecode (pc+1)) 32 | else if instr = 21 then let c = bytecode.(pc+1) in stack.(sp) <- c; interp stack (sp+1) bytecode (pc+2) 33 | else if instr = 22 then let n = stack.(sp-1) in let arr = cast_fIAI(stack.(sp-2)) in stack.(sp-2) <- arr.(n); interp stack (sp-1) bytecode (pc+1) 34 | else if instr = 23 then let i = stack.(sp-1) in let arr = cast_fIAI(stack.(sp-2)) in let n = stack.(sp-3) in arr.(i) <- n; stack.(sp-3) <- cast_fAII(arr); interp stack (sp-2) bytecode (pc+1) 35 | else if instr = 24 then let init = stack.(sp-1) in let size = stack.(sp-2) in let a = Array.make size init in stack.(sp-2) <- cast_fAII(a); interp stack (sp-1) bytecode (pc+1) 36 | else if instr = 26 then interp stack sp bytecode (pc+1) 37 | else if instr = 28 then let n = read_int () in stack.(sp) <- n ; interp stack (sp+1) bytecode (pc+1) 38 | else if instr = 30 then let v = stack.(sp-1) in print_int v ; interp stack (sp-1) bytecode (pc+1) 39 | else if instr = 31 then let _ = print_newline() in interp stack sp bytecode (pc+1) 40 | (* else if instr = 32 then let v = stack.(sp-1) in print_string v ; interp stack (sp-1) bytecode (pc+1) *) 41 | else -1000 in 42 | 43 | let stk = Array.make 20000 (-987) in 44 | let n = read_int () in 45 | let rec read_code i n arr = if i = n then arr else ((cast_fIAI arr).(i) <- read_int (); read_code (i+1) n arr) in 46 | let code = read_code 0 n (cast_fAII(Array.make n 0)) in 47 | let _ = (interp stk 1 (cast_fIAI code) 0) in 48 | print_newline () 49 | -------------------------------------------------------------------------------- /footer.pug: -------------------------------------------------------------------------------- 1 | link(rel='stylesheet' href='https://groupoid.space/main.css') 2 | 3 | footer.footer 4 | a(href='https://5HT.co/license/') 5 | img.footer__logo(src='https://longchenpa.guru/seal.png',width=50) 6 | span.footer__copy 2016—2025 © Namdak Tönpa 7 | script(src='https://groupoid.space/highlight.js?v=1') 8 | script(src='https://groupoid.space/bundle.js') 9 | -------------------------------------------------------------------------------- /framework.js: -------------------------------------------------------------------------------- 1 | // Mixin usages in PUG: 2 | 3 | // +tex(false, false). 4 | // $\mathbf{Definition}$ (Space of Sections). Let $\mathbf{H}$ be 5 | // a $(\infty,1)$-topos, and let $E \rightarrow B : \mathbf{H}_{/B}$ a bundle in 6 | // $\mathbf{H}$, object in the slice topos. Then the space of sections $\Gamma_\Sigma(E)$ 7 | // of this bundle is the Dependent Product. 8 | 9 | // +tex(true, false). 10 | // $$ 11 | // \Gamma_\Sigma(E) = \Pi_\Sigma (E) \in \mathbf{H}. 12 | // $$ 13 | 14 | // +code. 15 | // def Pi (A : U) (B : A → U) : U := Π (x : A), B x 16 | 17 | const {mathjax} = require('mathjax-full/js/mathjax.js'); 18 | const {TeX} = require('mathjax-full/js/input/tex.js'); 19 | const {SVG} = require('mathjax-full/js/output/svg.js'); 20 | const {liteAdaptor} = require('mathjax-full/js/adaptors/liteAdaptor.js'); 21 | const {RegisterHTMLHandler} = require('mathjax-full/js/handlers/html.js'); 22 | const {AssistiveMmlHandler} = require('mathjax-full/js/a11y/assistive-mml.js'); 23 | const {AllPackages} = require('mathjax-full/js/input/tex/AllPackages.js'); 24 | 25 | const adaptor = liteAdaptor(); 26 | const handler = RegisterHTMLHandler(adaptor); 27 | 28 | const tex = new TeX({ 29 | packages: ['base', 'autoload', 'require', 'ams', 'amscd', 'newcommand', 'configmacros'], 30 | inlineMath: [ ["$", "$"] ], 31 | macros: { // Plug your Glyphs here 32 | llparenthesis: '\\mathopen{\u2987}', 33 | rrparenthesis: '\\mathclose{\u2988}', 34 | llbracket: '\\mathopen{\u27E6}', 35 | rrbracket: '\\mathclose{\u27E7}', 36 | incmap: '\\mathclose{\u21AA}', 37 | meet: '\\mathopen{\u2227}', 38 | map: '\\mathopen{\u21A6}', 39 | join: '\\mathopen{\u2228}', 40 | trans: '\\, \\mathbin{\\vcenter{\\rule{.3ex}{.3ex}}} \\,', 41 | mapright: ['\\xrightarrow{{#1}}', 1], 42 | mapdown: ['\\Big\\downarrow\\rlap{\\raise2pt{\\scriptstyle{#1}}}', 1], 43 | mapdiagl: ['\\vcenter{\\searrow}\\rlap{\\raise2pt{\\scriptstyle{#1}}}', 1], 44 | mapdiagr: ['\\vcenter{\\swarrow}\\rlap{\\raise2pt{\\scriptstyle{#1}}}', 1], 45 | } 46 | }); 47 | 48 | tex.postFilters.add(({math, data}) => { 49 | if (!data.error) return; 50 | data.root.walkTree((node) => { 51 | if (node.isKind('merror')) { 52 | console.log('TeX error:\n ' + node.attributes.get('data-mjx-error')); 53 | } 54 | }); 55 | }); 56 | 57 | const svg = new SVG({fontCache: 'local'}); 58 | 59 | function renderPug(block) { 60 | var recv; with({pug_html: ""}){ 61 | eval(`(${block})();`); recv = pug_html; 62 | }; return recv 63 | } 64 | 65 | function renderTeX(formulae) { 66 | return adaptor.innerHTML(mathjax.document(formulae, { 67 | InputJax: tex, OutputJax: svg 68 | }).render().document.body); 69 | } 70 | 71 | exports.tex = function (block) { 72 | return renderTeX(renderPug(block)); 73 | } 74 | 75 | exports.highlight = function (block) { 76 | return renderPug(block) 77 | .replace(/([(){}→=]+|:|:=)/g, 78 | '$1') 79 | .replace(/\b(∀|Π|Σ|W|λ|glue|unglue|Glue|transp|hcomp|where|def|begin|end|module|import|option|false|true|indᵂ|sup|.1|.2|𝟎|𝟏|𝟐|ind₂|ind₁|ind₀|★|0₂|1₂|PathP|PartialP|inc|ouc|axiom|theorem|lemdata|ma|U|V)\b(?!:)/g, 80 | '$1'); 81 | } 82 | -------------------------------------------------------------------------------- /header.pug: -------------------------------------------------------------------------------- 1 | mixin tex(center=false, paragraph=true) 2 | if paragraph 3 | p(style=center ? {'text-align': 'center' } : null)!= tex(`${block}`) 4 | else 5 | span(style=center ? { 6 | 'text-align': 'center', 7 | 'display': 'block', 8 | 'padding-top': '8px', 9 | 'padding-bottom': '8px', 10 | } : null)!= tex(`${block}`) 11 | 12 | mixin tex2(center=false, paragraph=true) 13 | p(style=center ? { 14 | 'text-align': 'center', 15 | 'display': 'block', 16 | 'padding-top': '8px', 17 | 'padding-bottom': '8px', 18 | } : null)!= tex(`${block}`) 19 | 20 | mixin code 21 | code!= highlight(`${block}`) 22 | 23 | mixin header(logo, title, subtitle) 24 | header.header 25 | .header__titles 26 | h1.header__title!= title 27 | h4.header__subtitle!= subtitle 28 | 29 | doctype html 30 | html 31 | head 32 | meta(charset='utf-8') 33 | meta(http-equiv='x-ua-compatible' content='ie=edge') 34 | meta(property='fb:app_id' content='118554188236439') 35 | meta(name='viewport' content='width=device-width, initial-scale=1') 36 | meta(name='author' content='Maxim Sokhatsky') 37 | meta(name='twitter:site' content='@5HT') 38 | meta(name='twitter:creator' content='@5HT') 39 | meta(property='og:type' content='website') 40 | meta(property='og:image' content='https://avatars.githubusercontent.com/u/17128096?s=400&u=66a63d4cdd9625b2b4b37d724cc00fe6401e5bd8&v=4') 41 | meta(name='msapplication-TileColor' content='#ffffff') 42 | meta(name='msapplication-TileImage' content='https://anders.groupoid.space/images/ms-icon-144x144.png') 43 | meta(name='theme-color' content='#ffffff') 44 | 45 | link(rel='stylesheet' href='https://anders.groupoid.space/main.css?v=1') 46 | link(rel='apple-touch-icon' sizes='57x57' href='https://anders.groupoid.space/images/apple-icon-57x57.png') 47 | link(rel='apple-touch-icon' sizes='60x60' href='https://anders.groupoid.space/images/apple-icon-60x60.png') 48 | link(rel='apple-touch-icon' sizes='72x72' href='https://anders.groupoid.space/images/apple-icon-72x72.png') 49 | link(rel='apple-touch-icon' sizes='76x76' href='https://anders.groupoid.space/images/apple-icon-76x76.png') 50 | link(rel='apple-touch-icon' sizes='114x114' href='https://anders.groupoid.space/images/apple-icon-114x114.png') 51 | link(rel='apple-touch-icon' sizes='120x120' href='https://anders.groupoid.space/images/apple-icon-120x120.png') 52 | link(rel='apple-touch-icon' sizes='144x144' href='https://anders.groupoid.space/images/apple-icon-144x144.png') 53 | link(rel='apple-touch-icon' sizes='152x152' href='https://anders.groupoid.space/images/apple-icon-152x152.png') 54 | link(rel='apple-touch-icon' sizes='180x180' href='https://anders.groupoid.space/images//apple-icon-180x180.png') 55 | link(rel='icon' type='image/png' sizes='192x192' href='https://anders.groupoid.space/images/android-icon-192x192.png') 56 | link(rel='icon' type='image/png' sizes='32x32' href='https://anders.groupoid.space/images/favicon-32x32.png') 57 | link(rel='icon' type='image/png' sizes='96x96' href='https://anders.groupoid.space/images/favicon-96x96.png') 58 | link(rel='icon' type='image/png' sizes='16x16' href='https://anders.groupoid.space/images/favicon-16x16.png') 59 | link(rel='manifest' href='https://anders.groupoid.space/images/manifest.json') 60 | 61 | style. 62 | svg a{fill:blue;stroke:blue} 63 | [data-mml-node="merror"]>g{fill:red;stroke:red} 64 | [data-mml-node="merror"]>rect[data-background]{fill:yellow;stroke:none} 65 | [data-frame],[data-line]{stroke-width:70px;fill:none} 66 | .mjx-dashed{stroke-dasharray:140} 67 | .mjx-dotted{stroke-linecap:round;stroke-dasharray:0,140} 68 | use[data-c]{stroke-width:3px} 69 | 70 | body.content 71 | block vars 72 | block content 73 | -------------------------------------------------------------------------------- /index.pug: -------------------------------------------------------------------------------- 1 | include header 2 | 3 | html 4 | head 5 | meta(property='og:title' content='Joe') 6 | meta(property='og:description' content='Чиста нетипізована мова для декартово-замкнених категорій') 7 | meta(property='og:url' content='https://joe.groupoid.space/') 8 | 9 | block title 10 | title JOE 11 | 12 | block content 13 | +header('', 'Joe', 'Мінімальна мова для послідовних обчислень в декартово-замкнених категоріях') 14 | article.main 15 | .exe 16 | section 17 | h1 Анотація 18 | aside Намдак Тонпа 19 | time ДАТА: 10 ЖОВТНЯ 2016 20 | section 21 | +tex. 22 | Мова програмування $\mathbf{Joe}$ — це чиста нетипізована мова, що є внутрішньою мовою декартово-замкнених категорій. Вона базується на лямбда-численні, розширеному парами, проєкціями та термінальним об’єктом, забезпечуючи мінімальну модель для обчислень у категорійному контексті. 23 | 24 | .semantics 25 | section 26 | h2#ast Синтаксис 27 | +tex. 28 | Терми $\mathbf{Joe}$ складаються зі змінних, лямбда-абстракцій, застосувань, пар, проєкцій (першої та другої) та термінального об’єкта. Це мінімальна мова, що підтримує обчислення через бета-редукцію та проєкції. 29 | code. 30 | I = #identifier 31 | O = I | ( O ) | O O | λ I -> O | O , O | O.1 | O.2 | 1 32 | br. 33 | code. 34 | type term = 35 | | Var of string 36 | | Lam of string * term 37 | | App of term * term 38 | | Pair of term * term 39 | | Fst of term 40 | | Snd of term 41 | | Unit 42 | br. 43 | 44 | h2#rules Правила обчислень 45 | +tex. 46 | Основними правилами обчислень у $\mathbf{Joe}$ є бета-редукція для лямбда-абстракцій та правила проєкцій для пар. Термінальний об’єкт є незвідним. 47 | code. 48 | App (Lam (x, b), a) → subst x a b 49 | Fst (Pair (t1, t2)) → t1 50 | Snd (Pair (t1, t2)) → t2 51 | br. 52 | +tex(true). 53 | $$ 54 | \begin{align} 55 | \tag{$\beta$-reduction} 56 | \dfrac 57 | {(\lambda x . b)\ a} 58 | {b [a/x]} \\ 59 | \tag{proj-1} 60 | \dfrac 61 | {\text{fst}\ \langle t_1, t_2 \rangle} 62 | {t_1} \\ 63 | \tag{proj-2} 64 | \dfrac 65 | {\text{snd}\ \langle t_1, t_2 \rangle} 66 | {t_2} 67 | \end{align} 68 | $$ 69 | 70 | h2 Підстановка 71 | code. 72 | let rec subst x s = function 73 | | Var y -> if x = y then s else Var y 74 | | Lam (y, t) when x <> y -> Lam (y, subst x s t) 75 | | App (f, a) -> App (subst x s f, subst x s a) 76 | | Pair (t1, t2) -> Pair (subst x s t1, subst x s t2) 77 | | Fst t -> Fst (subst x s t) 78 | | Snd t -> Snd (subst x s t) 79 | | Unit -> Unit 80 | | t -> t 81 | br. 82 | 83 | h2 Рівність 84 | code. 85 | let rec equal t1 t2 = 86 | match t1, t2 with 87 | | Var x, Var y -> x = y 88 | | Lam (x, b), Lam (y, b') -> equal b (subst y (Var x) b') 89 | | Lam (x, b), t -> equal b (App (t, Var x)) 90 | | t, Lam (x, b) -> equal (App (t, Var x)) b 91 | | App (f1, a1), App (f2, a2) -> equal f1 f2 && equal a1 a2 92 | | Pair (t1, t2), Pair (t1', t2') -> equal t1 t1' && equal t2 t2' 93 | | Fst t, Fst t' -> equal t t' 94 | | Snd t, Snd t' -> equal t t' 95 | | Unit, Unit -> true 96 | | _ -> false 97 | br. 98 | 99 | h2 Редукція 100 | code. 101 | let rec reduce = function 102 | | App (Lam (x, b), a) -> subst x a b 103 | | App (f, a) -> App (reduce f, reduce a) 104 | | Pair (t1, t2) -> Pair (reduce t1, reduce t2) 105 | | Fst (Pair (t1, t2)) -> t1 106 | | Fst t -> Fst (reduce t) 107 | | Snd (Pair (t1, t2)) -> t2 108 | | Snd t -> Snd (reduce t) 109 | | Unit -> Unit 110 | | t -> t 111 | br. 112 | 113 | h2 Нормалізація 114 | code. 115 | let rec normalize t = 116 | let t' = reduce t in 117 | if equal t t' then t else normalize t' 118 | br. 119 | 120 | h2 Внутрішня мова ДЗК 121 | +tex. 122 | Мова $\mathbf{Joe}$ є внутрішньою мовою декартово-замкненої категорії (ДЗК). 123 | Вона включає лямбда-абстракції та застосування для замкнутої структури, 124 | пари та проєкції для декартового добутку, а також термінальний об’єкт для 125 | відновлення повної структури ДЗК. 126 | 127 | section 128 | h1 Бібліографія 129 | p(style="font-size:16px;"). 130 | [1]. Alonzo Church. A Set of Postulates for the Foundation of Logic. 1933
131 | [2]. Alonzo Church. An Unsolvable Problem of Elementary Number Theory. 1941
132 | [3]. Haskell Curry, Robert Fey. Combinatory Logic, Volume I. 1951
133 | [4]. Dana Scott. A Type-Free Theory of Lambda Calculus. 1970
134 | [5]. John Reynolds. Towards a Theory of Type Structure. 1974
135 | [6]. Henk Barendregt. The Lambda Calculus: Its Syntax and Semantics. 1984
136 | [7]. G.Cousineau, P.-L.Curien, M.Mauny. The Categorical Abstract Machine. 1985
137 | 138 | include footer 139 | -------------------------------------------------------------------------------- /joe.opam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/joe.opam -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'local'; 3 | src: url("https://groupoid.space/Geometria-Light.otf"); 4 | font-weight: normal; 5 | font-style: normal; } 6 | 7 | .MathJax_Display { 8 | overflow-x: auto; 9 | overflow-y: hidden; } 10 | 11 | nav a { 12 | font-size: 18px; 13 | border: 2px solid #dedede; 14 | background-color: white; 15 | color: lightblue; 16 | text-decoration: none; 17 | margin: 5px 5px; 18 | padding: 7px 12px; 19 | min-width: 150px; 20 | text-align: center; } 21 | 22 | nav a:visited { 23 | color: lightblue; } 24 | 25 | nav a:hover { 26 | border-bottom: 2px solid #00b8cf; } 27 | 28 | nav { 29 | display: flex; 30 | background-color: #FBFBFB; 31 | flex-direction: row; 32 | justify-content: center; } 33 | 34 | nav a { 35 | font-size: 18px; 36 | min-width: initial; } 37 | 38 | * { 39 | margin: 0; 40 | padding: 0; 41 | box-sizing: border-box; } 42 | 43 | sub, 44 | sup { 45 | font-size: 80%; 46 | line-height: 0; 47 | position: relative; 48 | vertical-align: baseline; } 49 | 50 | sub { 51 | bottom: -0.25em; } 52 | 53 | sup { 54 | top: -0.5em; } 55 | 56 | html { 57 | height: 100%; } 58 | 59 | body { 60 | min-height: 100%; 61 | text-rendering: optimizeLegibility; 62 | -webkit-font-smoothing: antialiased; } 63 | 64 | h1, h2, h3, h4 { 65 | font-weight: normal; } 66 | 67 | ul { 68 | list-style-type: none; } 69 | 70 | ol, 71 | ul { 72 | text-align: left; 73 | line-height: 1.5; } 74 | 75 | img, figure { 76 | vertical-align: middle; } 77 | 78 | img { 79 | margin-left: 20px; } 80 | 81 | .content { 82 | min-height: 100%; 83 | font-family: local; 84 | display: flex; 85 | flex-direction: column; 86 | position: relative; } 87 | 88 | .main { 89 | color: #586E75; 90 | background: #FBFBFB; 91 | padding: 50px 0px; 92 | text-align: center; 93 | box-shadow: 0 3px 10px rgba(0, 0, 0, 0); 94 | z-index: 5; 95 | flex: 1 1 auto; } 96 | 97 | figure { 98 | min-width: 300px; 99 | overflow-x: auto; 100 | padding: 10px 2px; 101 | font-size: 18px; 102 | display: block; } 103 | 104 | figure::after { 105 | content: "\A"; 106 | white-space: pre; } 107 | 108 | h2 { 109 | font-size: 24px; 110 | color: #268BD2; 111 | margin: 34px 0 8px; } 112 | @media (min-width: 768px) { 113 | h2 { 114 | font-size: 32px; } } 115 | 116 | h3 { 117 | max-width: 100%; 118 | margin: 16px auto 0; 119 | padding: 0 0 0 8px; 120 | text-transform: uppercase; 121 | text-align: left; 122 | font-size: 22px; } 123 | 124 | p { 125 | max-width: 100%; 126 | width: 650px; 127 | margin: auto; 128 | padding: 8px; 129 | text-align: left; 130 | font-size: 18px; 131 | line-height: 1.4; } 132 | @media (min-width: 768px) { 133 | p { 134 | font-size: 22px; } } 135 | 136 | p:hover { 137 | background: white; } 138 | 139 | mark { 140 | background: #FDF6E3; 141 | padding: 0px 2px; 142 | border-radius: 2px; 143 | color: #586E75; } 144 | 145 | a { 146 | color: #3A98C8; 147 | text-decoration: none; } 148 | a:visited { 149 | color: #3A98C8; } 150 | a:hover { 151 | color: #D33682; } 152 | a:active { 153 | color: #D33682; } 154 | 155 | aside { 156 | max-width: 650px; 157 | margin: auto; 158 | padding: 8px; 159 | font-size: 20px; 160 | text-align: right; } 161 | aside time, aside div { 162 | margin-bottom: 20px; 163 | display: block; } 164 | 165 | .header { 166 | background: black; 167 | color: white; 168 | position: relative; 169 | min-height: 450px; 170 | padding: 16px 8px; 171 | display: flex; 172 | text-align: center; 173 | flex-direction: column; 174 | justify-content: center; 175 | align-items: center; 176 | width: 100%; 177 | z-index: 5; } 178 | .header__logo { 179 | width: 130px; 180 | position: relative; } 181 | .header__titles { 182 | position: relative; 183 | max-width: 800px; 184 | margin: 20px; 185 | text-shadow: 1px 1px 7px #56CCF2; } 186 | .header__title { 187 | font-size: 48px; 188 | line-height: 1.1; } 189 | .header__subtitle { 190 | font-size: 22px; } 191 | @media (min-width: 768px) { 192 | .header { 193 | flex-direction: row; } 194 | .header__logo { 195 | width: 160px; 196 | margin-right: 10px; } 197 | .header__title { 198 | font-size: 60px; } 199 | .header__subtitle { 200 | font-size: 30px; } } 201 | 202 | .footer { 203 | width: 100%; 204 | padding: 15px; 205 | background: #7F8C8D; 206 | color: white; 207 | text-align: center; } 208 | .footer__logo { 209 | width: 50px; 210 | margin: 20px; } 211 | .footer__copy { 212 | font-size: 16px; 213 | white-space: pre; } 214 | @media (min-width: 768px) { 215 | .footer__copy { 216 | font-size: 24px; } } 217 | 218 | .semantics { 219 | text-align: center; } 220 | .semantics figure { 221 | display: inline-block; 222 | max-width: min-content; 223 | font-size: 13px; } 224 | @media (min-width: 800px) { 225 | .semantics figure { 226 | font-size: 16px; } } 227 | .semantics h1 { 228 | color: #7D8A96; 229 | font-size: 32px; 230 | text-transform: uppercase; 231 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); 232 | display: inline-block; 233 | margin-bottom: 8px; } 234 | .semantics section { 235 | margin-top: 40px; } 236 | 237 | .intro { 238 | width: 650px; 239 | max-width: 100%; 240 | margin: auto; } 241 | 242 | .status { 243 | text-align: left; 244 | display: inline-block; 245 | padding-left: 32px; } 246 | .status ol { 247 | line-height: 1.5; 248 | font-size: 18px; } 249 | @media (min-width: 768px) { 250 | .status ol { 251 | font-size: 22px; } } 252 | 253 | .resources { 254 | max-width: 650px; 255 | margin: 32px auto 0; 256 | text-align: left; 257 | padding: 8px; } 258 | .resources__title { 259 | display: inline-block; 260 | margin-bottom: 10px; 261 | color: #7D8A96; 262 | font-size: 32px; 263 | text-transform: uppercase; 264 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); } 265 | @media (min-width: 768px) { 266 | .resources__title { 267 | font-size: 40px; } } 268 | .resources__list { 269 | padding-left: 32px; 270 | line-height: 1.5; 271 | font-size: 18px; } 272 | @media (min-width: 768px) { 273 | .resources__list { 274 | font-size: 22px; } } 275 | 276 | .index { 277 | max-width: 900px; 278 | margin: auto; 279 | padding: 20px 0; 280 | display: flex; 281 | flex-wrap: wrap; 282 | justify-content: center; } 283 | .index__col { 284 | white-space: nowrap; 285 | padding: 15px 25px; 286 | margin: 15px; 287 | background: #fefefe; 288 | box-shadow: 0 1px 4px rgba(0, 0, 100, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 289 | transition: box-shadow 0.5s cubic-bezier(0.23, 1, 0.32, 1); } 290 | .index__col:hover { 291 | box-shadow: 0 8px 40px rgba(0, 0, 100, 0.15), inset 0 0 0 1px rgba(0, 0, 0, 0.1); } 292 | .index a { 293 | line-height: 1.5; 294 | color: #888; 295 | font-size: 24px; } 296 | .index a[href^="#"] { 297 | color: #BBBBBB; } 298 | .index a:hover { 299 | color: #3A98C8; } 300 | .index a[href^="#"]:hover { 301 | color: #BBBBBB; } 302 | .index h2 { 303 | font-size: 24px; 304 | text-align: left; 305 | color: #707070; 306 | margin: 0 0 10px; 307 | text-transform: uppercase; } 308 | 309 | .om figure { 310 | max-width: inherit; } 311 | 312 | .om section h1 { 313 | margin-top: 50px; } 314 | 315 | .om h1 { 316 | color: #7D8A96; 317 | font-size: 32px; 318 | text-transform: uppercase; 319 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); 320 | display: inline-block; 321 | margin-bottom: 32px; } 322 | @media (min-width: 768px) { 323 | .om h1 { 324 | font-size: 40px; } } 325 | 326 | .om h1 + h2 { 327 | margin-top: 20px; } 328 | 329 | .om h3 { 330 | max-width: 100%; 331 | margin: auto; 332 | width: 600px; 333 | padding: 16px 8px 0; } 334 | 335 | .exe figure { 336 | max-width: inherit; } 337 | 338 | .exe h1 { 339 | color: #7D8A96; 340 | font-size: 32px; 341 | text-transform: uppercase; 342 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); 343 | display: inline-block; 344 | margin: 42px 0 10px; } 345 | @media (min-width: 768px) { 346 | .exe h1 { 347 | font-size: 40px; } } 348 | 349 | .exe h1 + h2 { 350 | margin-top: 0px; } 351 | 352 | .macro { 353 | margin: 0 auto; 354 | display: flex; 355 | justify-content: space-around; 356 | max-width: 600px; 357 | flex-wrap: wrap; 358 | width: 100%; } 359 | .macro__col { 360 | padding-left: 24px; 361 | line-height: 1.5; 362 | font-size: 14px; } 363 | .macro__col h3 { 364 | padding: 0; 365 | width: initial; } 366 | @media (min-width: 768px) { 367 | .macro__col { 368 | font-size: 22px; } } 369 | 370 | @media (min-width: 920px) { 371 | .langf { 372 | display: flex; 373 | flex-direction: row; 374 | justify-content: center; } } 375 | 376 | .langf ol { 377 | display: inline-block; 378 | max-width: 650px; 379 | margin: auto; 380 | font-size: 20px; 381 | padding-left: 36px; } 382 | 383 | @media (min-width: 920px) { 384 | .langf__col .langf-col:last-child { 385 | white-space: pre; 386 | margin-left: 20px; } } 387 | 388 | .types { 389 | display: inline-block; 390 | text-align: left; 391 | max-width: 100%; 392 | padding: 0 8px; } 393 | .types h1 { 394 | color: #7D8A96; 395 | font-size: 32px; 396 | text-transform: uppercase; 397 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); 398 | display: inline-block; 399 | margin: 42px 0 10px; } 400 | @media (min-width: 768px) { 401 | .types h1 { 402 | font-size: 40px; } } 403 | .types section { 404 | max-width: 100%; } 405 | .types p { 406 | margin: 0; 407 | padding-left: 0; } 408 | .types h1 + h2 { 409 | margin-top: 0px; } 410 | .types .type { 411 | max-width: 660px; 412 | display: flex; 413 | flex-flow: row wrap; } 414 | .types .type__col { 415 | padding-left: 22px; 416 | flex: 1 0 15%; } 417 | .types .type__col h3 { 418 | width: initial; 419 | padding: 0; 420 | margin: 0; } 421 | @media (min-width: 600px) { 422 | .types .type__col { 423 | padding-right: 22px; 424 | flex: 1 0 15%; 425 | font-size: 20px; } } 426 | .types .legend { 427 | margin: 20px auto 0; 428 | display: inline-block; 429 | padding: 10px 20px; 430 | font-size: 24px; 431 | background: #FDF6E3; 432 | border-radius: 4px; 433 | box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); } 434 | 435 | .list h1 { 436 | color: #7D8A96; 437 | font-size: 32px; 438 | text-transform: uppercase; 439 | border-bottom: 1px solid rgba(0, 0, 0, 0.3); 440 | display: inline-block; 441 | margin: 42px 8px 10px; } 442 | @media (min-width: 768px) { 443 | .list h1 { 444 | font-size: 40px; } } 445 | 446 | code { 447 | display: inline-block; 448 | overflow-x: auto; 449 | max-width: 100%; 450 | padding: 12px; 451 | margin: 8px 0; 452 | white-space: pre; 453 | text-align: left; 454 | border-radius: 4px; 455 | font-size: 14px; 456 | color: #00259c; 457 | background: white; 458 | box-shadow: 0 1px 4px #f1f0f0, inset 0 0 0 1px #e7e6e5; } 459 | @media (min-width: 768px) { 460 | code { 461 | font-size: 17px; } } 462 | 463 | pre { 464 | white-space: normal; 465 | max-width: 100%; 466 | width: 650px; 467 | padding: 12px; 468 | margin: 8px auto; 469 | text-align: left; 470 | border-radius: 4px; 471 | font-size: 14px; 472 | color: #00259c; 473 | background: white; 474 | box-shadow: 0 1px 4px #f1f0f0, inset 0 0 0 1px #e7e6e5; } 475 | @media (min-width: 768px) { 476 | pre { 477 | font-size: 17px; } } 478 | 479 | .h__name { 480 | color: #00259c; 481 | font-weight: bold; } 482 | 483 | .h__keyword { 484 | color: #ca30d4; } 485 | 486 | .h__symbol { 487 | color: #9f9fa3; } 488 | 489 | canvas { 490 | max-width: 100%; 491 | position: absolute; 492 | z-index: -10; } 493 | 494 | .stack { 495 | margin: auto; 496 | max-width: 100%; 497 | border-spacing: 10px; 498 | color: #282828; 499 | font-size: 20px; } 500 | .stack td { 501 | background-color: #fff9a6; 502 | padding: 4px; 503 | outline: 1px solid rgba(0, 0, 0, 0.3); } 504 | .stack th { 505 | padding: 4px; 506 | text-align: left; 507 | font-weight: normal; } 508 | .stack .empty { 509 | background-color: inherit; 510 | outline: inherit; } 511 | 512 | @media (max-width: 600px) { 513 | .stack { 514 | font-size: 16px; 515 | display: inline-block; 516 | border-spacing: 0; } 517 | .stack tbody { 518 | display: block; } 519 | .stack tr { 520 | display: block; 521 | text-align: left; 522 | vertical-align: top; 523 | position: relative; } 524 | .stack td { 525 | margin: 30px 0 10px; 526 | text-align: center; 527 | padding: 4px; 528 | display: inline-block; 529 | width: 90px; } 530 | .stack td[colspan="4"] { 531 | width: 360px; } 532 | .stack td[colspan="2"] { 533 | width: 180px; } 534 | .stack th { 535 | position: absolute; 536 | top: 0; 537 | width: 100%; } } 538 | 539 | @media (max-width: 320px) { 540 | .stack { 541 | font-size: 12px; } 542 | .stack td { 543 | margin: 20px 0 5px; 544 | width: 80px; } 545 | .stack td[colspan="4"] { 546 | width: 320px; } 547 | .stack td[colspan="2"] { 548 | width: 160px; } } 549 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bob", 3 | "version": "1.0.1", 4 | "description": "Groupoid Infinity Institute", 5 | "main": "", 6 | "scripts": { 7 | "start": "node-sass ./styles -o ./ && pug -O ./framework.js index.pug ", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/groupoid/bob" 13 | }, 14 | "author": "Namdak Tonpa", 15 | "license": "DHARMA", 16 | "preinstall": "npm i -g node-sass && npm i -g pug-cli", 17 | "dependencies": { 18 | "mathjax-full": "^3.2.0", 19 | "npm": "^8.12.1", 20 | "pug": "^3.0.2", 21 | "pug-cli": "^1.0.0-alpha6", 22 | "sass": "^1.52.2" 23 | }, 24 | "devDependencies": { 25 | "node-sass": "^7.0.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/arm64/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name arm64) 3 | (public_name joe.arm64) 4 | (flags (-w -4-33-40-41)) 5 | (libraries str MinCaml) 6 | (foreign_stubs (language c) (names libmincaml)) 7 | (preprocess (pps ppx_deriving.show))) 8 | -------------------------------------------------------------------------------- /src/arm64/emit.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | 3 | val f : out_channel -> Asm.prog -> unit 4 | -------------------------------------------------------------------------------- /src/arm64/libmincaml.S: -------------------------------------------------------------------------------- 1 | .section __TEXT,__text,regular,pure_instructions 2 | .build_version macos, 12, 0 sdk_version 13, 1 3 | .globl min_caml_print_int ; -- Begin function min_caml_print_int 4 | .p2align 2 5 | min_caml_print_int: ; @"\01min_caml_print_int" 6 | .cfi_startproc 7 | ; %bb.0: 8 | sub sp, sp, #32 9 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 10 | add x29, sp, #16 11 | .cfi_def_cfa w29, 16 12 | .cfi_offset w30, -8 13 | .cfi_offset w29, -16 14 | str x0, [sp, #8] 15 | ldr x8, [sp, #8] 16 | mov x9, sp 17 | str x8, [x9] 18 | adrp x0, l_.str@PAGE 19 | add x0, x0, l_.str@PAGEOFF 20 | bl _printf 21 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 22 | add sp, sp, #32 23 | ret 24 | .cfi_endproc 25 | ; -- End function 26 | .globl min_caml_print_newline ; -- Begin function min_caml_print_newline 27 | .p2align 2 28 | min_caml_print_newline: ; @"\01min_caml_print_newline" 29 | .cfi_startproc 30 | ; %bb.0: 31 | stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill 32 | mov x29, sp 33 | .cfi_def_cfa w29, 16 34 | .cfi_offset w30, -8 35 | .cfi_offset w29, -16 36 | adrp x0, l_.str.1@PAGE 37 | add x0, x0, l_.str.1@PAGEOFF 38 | bl _printf 39 | ldp x29, x30, [sp], #16 ; 16-byte Folded Reload 40 | ret 41 | .cfi_endproc 42 | ; -- End function 43 | .globl min_caml_create_array ; -- Begin function min_caml_create_array 44 | .p2align 2 45 | min_caml_create_array: ; @"\01min_caml_create_array" 46 | .cfi_startproc 47 | ; %bb.0: 48 | sub sp, sp, #48 49 | .cfi_def_cfa_offset 48 50 | str x0, [sp, #40] 51 | str x1, [sp, #32] 52 | ; InlineAsm Start 53 | mov x8, x27 54 | ; InlineAsm End 55 | str x8, [sp, #24] 56 | ldr x8, [sp, #24] 57 | str x8, [sp, #16] 58 | str xzr, [sp, #8] 59 | b LBB2_1 60 | LBB2_1: ; =>This Inner Loop Header: Depth=1 61 | ldr x8, [sp, #8] 62 | ldr x9, [sp, #40] 63 | subs x8, x8, x9 64 | b.ge LBB2_4 65 | b LBB2_2 66 | LBB2_2: ; in Loop: Header=BB2_1 Depth=1 67 | ldr x8, [sp, #32] 68 | ldr x9, [sp, #24] 69 | str x8, [x9] 70 | ldr x8, [sp, #24] 71 | add x8, x8, #8 72 | str x8, [sp, #24] 73 | b LBB2_3 74 | LBB2_3: ; in Loop: Header=BB2_1 Depth=1 75 | ldr x8, [sp, #8] 76 | add x8, x8, #1 77 | str x8, [sp, #8] 78 | b LBB2_1 79 | LBB2_4: 80 | ldr x8, [sp, #24] 81 | ; InlineAsm Start 82 | mov x27, x8 83 | ; InlineAsm End 84 | ldr x0, [sp, #16] 85 | add sp, sp, #48 86 | ret 87 | .cfi_endproc 88 | ; -- End function 89 | .globl min_caml_create_float_array ; -- Begin function min_caml_create_float_array 90 | .p2align 2 91 | min_caml_create_float_array: ; @"\01min_caml_create_float_array" 92 | .cfi_startproc 93 | ; %bb.0: 94 | sub sp, sp, #48 95 | .cfi_def_cfa_offset 48 96 | str x0, [sp, #40] 97 | str d0, [sp, #32] 98 | ; InlineAsm Start 99 | mov x8, x27 100 | ; InlineAsm End 101 | str x8, [sp, #24] 102 | ldr x8, [sp, #24] 103 | str x8, [sp, #16] 104 | str xzr, [sp, #8] 105 | b LBB3_1 106 | LBB3_1: ; =>This Inner Loop Header: Depth=1 107 | ldr x8, [sp, #8] 108 | ldr x9, [sp, #40] 109 | subs x8, x8, x9 110 | b.ge LBB3_4 111 | b LBB3_2 112 | LBB3_2: ; in Loop: Header=BB3_1 Depth=1 113 | ldr d0, [sp, #32] 114 | ldr x8, [sp, #24] 115 | str d0, [x8] 116 | ldr x8, [sp, #24] 117 | add x8, x8, #8 118 | str x8, [sp, #24] 119 | b LBB3_3 120 | LBB3_3: ; in Loop: Header=BB3_1 Depth=1 121 | ldr x8, [sp, #8] 122 | add x8, x8, #1 123 | str x8, [sp, #8] 124 | b LBB3_1 125 | LBB3_4: 126 | ldr x8, [sp, #24] 127 | ; InlineAsm Start 128 | mov x27, x8 129 | ; InlineAsm End 130 | ldr x0, [sp, #16] 131 | add sp, sp, #48 132 | ret 133 | .cfi_endproc 134 | ; -- End function 135 | .globl min_caml_truncate ; -- Begin function min_caml_truncate 136 | .p2align 2 137 | min_caml_truncate: ; @"\01min_caml_truncate" 138 | .cfi_startproc 139 | ; %bb.0: 140 | sub sp, sp, #16 141 | .cfi_def_cfa_offset 16 142 | str d0, [sp, #8] 143 | ldr d0, [sp, #8] 144 | fcvtzs x0, d0 145 | add sp, sp, #16 146 | ret 147 | .cfi_endproc 148 | ; -- End function 149 | .globl min_caml_print_float ; -- Begin function min_caml_print_float 150 | .p2align 2 151 | min_caml_print_float: ; @"\01min_caml_print_float" 152 | .cfi_startproc 153 | ; %bb.0: 154 | sub sp, sp, #32 155 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 156 | add x29, sp, #16 157 | .cfi_def_cfa w29, 16 158 | .cfi_offset w30, -8 159 | .cfi_offset w29, -16 160 | str d0, [sp, #8] 161 | ldr d0, [sp, #8] 162 | mov x8, sp 163 | str d0, [x8] 164 | adrp x0, l_.str.2@PAGE 165 | add x0, x0, l_.str.2@PAGEOFF 166 | bl _printf 167 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 168 | add sp, sp, #32 169 | ret 170 | .cfi_endproc 171 | ; -- End function 172 | .globl min_caml_print_byte ; -- Begin function min_caml_print_byte 173 | .p2align 2 174 | min_caml_print_byte: ; @"\01min_caml_print_byte" 175 | .cfi_startproc 176 | ; %bb.0: 177 | sub sp, sp, #32 178 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 179 | add x29, sp, #16 180 | .cfi_def_cfa w29, 16 181 | .cfi_offset w30, -8 182 | .cfi_offset w29, -16 183 | str x0, [sp, #8] 184 | ldr x8, [sp, #8] 185 | mov x0, x8 186 | bl _putchar 187 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 188 | add sp, sp, #32 189 | ret 190 | .cfi_endproc 191 | ; -- End function 192 | .globl min_caml_read_int ; -- Begin function min_caml_read_int 193 | .p2align 2 194 | min_caml_read_int: ; @"\01min_caml_read_int" 195 | .cfi_startproc 196 | ; %bb.0: 197 | sub sp, sp, #32 198 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 199 | add x29, sp, #16 200 | .cfi_def_cfa w29, 16 201 | .cfi_offset w30, -8 202 | .cfi_offset w29, -16 203 | mov x9, sp 204 | add x8, sp, #8 205 | str x8, [x9] 206 | adrp x0, l_.str@PAGE 207 | add x0, x0, l_.str@PAGEOFF 208 | bl _scanf 209 | ldr x0, [sp, #8] 210 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 211 | add sp, sp, #32 212 | ret 213 | .cfi_endproc 214 | ; -- End function 215 | .globl min_caml_read_float ; -- Begin function min_caml_read_float 216 | .p2align 2 217 | min_caml_read_float: ; @"\01min_caml_read_float" 218 | .cfi_startproc 219 | ; %bb.0: 220 | sub sp, sp, #32 221 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 222 | add x29, sp, #16 223 | .cfi_def_cfa w29, 16 224 | .cfi_offset w30, -8 225 | .cfi_offset w29, -16 226 | mov x9, sp 227 | add x8, sp, #8 228 | str x8, [x9] 229 | adrp x0, l_.str.2@PAGE 230 | add x0, x0, l_.str.2@PAGEOFF 231 | bl _scanf 232 | ldr d0, [sp, #8] 233 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 234 | add sp, sp, #32 235 | ret 236 | .cfi_endproc 237 | ; -- End function 238 | .globl min_caml_atan ; -- Begin function min_caml_atan 239 | .p2align 2 240 | min_caml_atan: ; @"\01min_caml_atan" 241 | .cfi_startproc 242 | ; %bb.0: 243 | sub sp, sp, #32 244 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 245 | add x29, sp, #16 246 | .cfi_def_cfa w29, 16 247 | .cfi_offset w30, -8 248 | .cfi_offset w29, -16 249 | str d0, [sp, #8] 250 | ldr d0, [sp, #8] 251 | bl _atan 252 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 253 | add sp, sp, #32 254 | ret 255 | .cfi_endproc 256 | ; -- End function 257 | .globl min_caml_cos ; -- Begin function min_caml_cos 258 | .p2align 2 259 | min_caml_cos: ; @"\01min_caml_cos" 260 | .cfi_startproc 261 | ; %bb.0: 262 | sub sp, sp, #32 263 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 264 | add x29, sp, #16 265 | .cfi_def_cfa w29, 16 266 | .cfi_offset w30, -8 267 | .cfi_offset w29, -16 268 | str d0, [sp, #8] 269 | ldr d0, [sp, #8] 270 | bl _cos 271 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 272 | add sp, sp, #32 273 | ret 274 | .cfi_endproc 275 | ; -- End function 276 | .globl min_caml_floor ; -- Begin function min_caml_floor 277 | .p2align 2 278 | min_caml_floor: ; @"\01min_caml_floor" 279 | .cfi_startproc 280 | ; %bb.0: 281 | sub sp, sp, #16 282 | .cfi_def_cfa_offset 16 283 | str d0, [sp, #8] 284 | ldr d0, [sp, #8] 285 | frintm d0, d0 286 | add sp, sp, #16 287 | ret 288 | .cfi_endproc 289 | ; -- End function 290 | .globl min_caml_sin ; -- Begin function min_caml_sin 291 | .p2align 2 292 | min_caml_sin: ; @"\01min_caml_sin" 293 | .cfi_startproc 294 | ; %bb.0: 295 | sub sp, sp, #32 296 | stp x29, x30, [sp, #16] ; 16-byte Folded Spill 297 | add x29, sp, #16 298 | .cfi_def_cfa w29, 16 299 | .cfi_offset w30, -8 300 | .cfi_offset w29, -16 301 | str d0, [sp, #8] 302 | ldr d0, [sp, #8] 303 | bl _sin 304 | ldp x29, x30, [sp, #16] ; 16-byte Folded Reload 305 | add sp, sp, #32 306 | ret 307 | .cfi_endproc 308 | ; -- End function 309 | .globl min_caml_abs_float ; -- Begin function min_caml_abs_float 310 | .p2align 2 311 | min_caml_abs_float: ; @"\01min_caml_abs_float" 312 | .cfi_startproc 313 | ; %bb.0: 314 | sub sp, sp, #16 315 | .cfi_def_cfa_offset 16 316 | str d0, [sp, #8] 317 | ldr d0, [sp, #8] 318 | fabs d0, d0 319 | add sp, sp, #16 320 | ret 321 | .cfi_endproc 322 | ; -- End function 323 | .globl min_caml_float_of_int ; -- Begin function min_caml_float_of_int 324 | .p2align 2 325 | min_caml_float_of_int: ; @"\01min_caml_float_of_int" 326 | .cfi_startproc 327 | ; %bb.0: 328 | sub sp, sp, #16 329 | .cfi_def_cfa_offset 16 330 | str x0, [sp, #8] 331 | ldr d0, [sp, #8] 332 | scvtf d0, d0 333 | add sp, sp, #16 334 | ret 335 | .cfi_endproc 336 | ; -- End function 337 | .globl min_caml_int_of_float ; -- Begin function min_caml_int_of_float 338 | .p2align 2 339 | min_caml_int_of_float: ; @"\01min_caml_int_of_float" 340 | .cfi_startproc 341 | ; %bb.0: 342 | sub sp, sp, #16 343 | .cfi_def_cfa_offset 16 344 | str d0, [sp, #8] 345 | ldr d0, [sp, #8] 346 | fcvtzs x0, d0 347 | add sp, sp, #16 348 | ret 349 | .cfi_endproc 350 | ; -- End function 351 | .globl min_caml_sqrt ; -- Begin function min_caml_sqrt 352 | .p2align 2 353 | min_caml_sqrt: ; @"\01min_caml_sqrt" 354 | .cfi_startproc 355 | ; %bb.0: 356 | sub sp, sp, #16 357 | .cfi_def_cfa_offset 16 358 | str d0, [sp, #8] 359 | ldr d0, [sp, #8] 360 | fsqrt d0, d0 361 | add sp, sp, #16 362 | ret 363 | .cfi_endproc 364 | ; -- End function 365 | .section __TEXT,__cstring,cstring_literals 366 | l_.str: ; @.str 367 | .asciz "%ld" 368 | 369 | l_.str.1: ; @.str.1 370 | .asciz "\n" 371 | 372 | l_.str.2: ; @.str.2 373 | .asciz "%lf" 374 | 375 | .subsections_via_symbols 376 | -------------------------------------------------------------------------------- /src/arm64/libmincaml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NOTE: コンパイル後のシンボルのプリフィックスに _ がつくのを避ける 5 | void min_caml_print_int(long n) asm("min_caml_print_int"); 6 | void min_caml_print_newline() asm("min_caml_print_newline"); 7 | long* min_caml_create_array(long number_of_element, long init_value) asm("min_caml_create_array"); 8 | double* min_caml_create_float_array(long number_of_element, double float_value) asm("min_caml_create_float_array"); 9 | long min_caml_truncate(double d) asm("min_caml_truncate"); 10 | void min_caml_print_float(double d) asm("min_caml_print_float"); 11 | void min_caml_print_byte(long n) asm("min_caml_print_byte"); 12 | long min_caml_read_int() asm("min_caml_read_int"); 13 | double min_caml_read_float() asm("min_caml_read_float"); 14 | double min_caml_atan(double x) asm("min_caml_atan"); 15 | long min_caml_mul(long x, long y) asm("min_caml_mul"); 16 | long min_caml_div(long x, long y) asm("min_caml_div"); 17 | long min_caml_rem(long x, long y) asm("min_caml_rem"); 18 | double min_caml_cos(double x) asm("min_caml_cos"); 19 | double min_caml_floor(double x) asm("min_caml_floor"); 20 | double min_caml_sin(double x) asm("min_caml_sin"); 21 | double min_caml_abs_float(double x) asm("min_caml_abs_float"); 22 | double min_caml_float_of_int(long n) asm("min_caml_float_of_int"); 23 | long min_caml_int_of_float(double d) asm("min_caml_int_of_float"); 24 | double min_caml_sqrt(double d) asm("min_caml_sqrt"); 25 | 26 | void min_caml_print_int(long n) { 27 | printf("%ld", n); 28 | } 29 | 30 | void min_caml_print_newline() { 31 | printf("\n"); 32 | } 33 | 34 | long* min_caml_create_array(long number_of_element, long init_value) { 35 | long *heap_ptr; 36 | 37 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 38 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 39 | 40 | // Array の先頭アドレスを取得 41 | long *array_ptr = heap_ptr; 42 | 43 | for (long i = 0l; i < number_of_element; i++) { 44 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 45 | *heap_ptr = init_value; 46 | heap_ptr += 1; 47 | } 48 | 49 | // ヒープの先頭アドレスを x27 に書き戻す 50 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 51 | 52 | return array_ptr; 53 | } 54 | 55 | double* min_caml_create_float_array(long number_of_element, double float_value) { 56 | double *heap_ptr; 57 | 58 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 59 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 60 | 61 | // Array の先頭アドレスを取得 62 | double *array_ptr = heap_ptr; 63 | 64 | for (long i = 0l; i < number_of_element; i++) { 65 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 66 | *heap_ptr = float_value; 67 | heap_ptr += 1; 68 | } 69 | 70 | // ヒープの先頭アドレスを x27 に書き戻す 71 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 72 | 73 | return array_ptr; 74 | } 75 | 76 | // truncate 77 | long min_caml_truncate(double d) { 78 | return (long)d; 79 | } 80 | 81 | void min_caml_print_float(double d) { 82 | printf("%lf", d); 83 | } 84 | 85 | void min_caml_print_byte(long n) { 86 | putchar(n); 87 | } 88 | 89 | long min_caml_read_int() { 90 | long l; 91 | // fscanf(fp, "%ld", &l); 92 | scanf("%ld", &l); 93 | return l; 94 | } 95 | 96 | double min_caml_read_float() { 97 | double d; 98 | // fscanf(fp, "%lf", &d); 99 | scanf("%lf", &d); 100 | return d; 101 | } 102 | 103 | // atan 104 | double min_caml_atan(double x) { 105 | return atan(x); 106 | } 107 | 108 | // cos 109 | double min_caml_cos(double x) { 110 | return cos(x); 111 | } 112 | 113 | // floor 114 | double min_caml_floor(double x) { 115 | return floor(x); 116 | } 117 | 118 | // sin 119 | double min_caml_sin(double x) { 120 | return sin(x); 121 | } 122 | 123 | // abs_float 124 | double min_caml_abs_float(double x) { 125 | return fabs(x); 126 | } 127 | 128 | // float_of_int 129 | double min_caml_float_of_int(long n) { 130 | return (double)n; 131 | } 132 | 133 | // int_of_float 134 | long min_caml_int_of_float(double d) { 135 | return (long)d; 136 | } 137 | 138 | long min_caml_div(long x, long y) { 139 | return (long)(x/y); 140 | } 141 | 142 | long min_caml_rem(long x, long y) { 143 | return x % y; 144 | } 145 | 146 | long min_caml_mul(long x, long y) { 147 | return x * y; 148 | } 149 | 150 | // sqrt 151 | double min_caml_sqrt(double d) { 152 | return sqrt(d); 153 | } 154 | -------------------------------------------------------------------------------- /src/arm64/regAlloc.ml: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | open Asm 3 | open Asm.ARM64 4 | 5 | (* for register coalescing *) 6 | (* [XXX] Callがあったら、そこから先は無意味というか逆効果なので追わない。 7 | そのために「Callがあったかどうか」を返り値の第1要素に含める。 *) 8 | let rec target' src (dest, t) = function 9 | | Mr(x) when x = src && is_reg dest -> 10 | assert (t <> Type.Unit); 11 | assert (t <> Type.Float); 12 | false, [dest] 13 | | FMr(x) when x = src && is_reg dest -> 14 | assert (t = Type.Float); 15 | false, [dest] 16 | | IfEq(_, _, e1, e2) | IfLE(_, _, e1, e2) | IfGE(_, _, e1, e2) 17 | | IfFEq(_, _, e1, e2) | IfFLE(_, _, e1, e2) -> 18 | let c1, rs1 = target src (dest, t) e1 in 19 | let c2, rs2 = target src (dest, t) e2 in 20 | c1 && c2, rs1 @ rs2 21 | | CallCls(x, ys, zs) -> 22 | true, (target_args src regs 0 ys @ 23 | target_args src fregs 0 zs @ 24 | if x = src then [reg_cl] else []) 25 | | CallDir(_, ys, zs) -> 26 | true, (target_args src regs 0 ys @ 27 | target_args src fregs 0 zs) 28 | | _ -> false, [] 29 | 30 | and target src dest = function (* register targeting (caml2html: regalloc_target) *) 31 | | Ans(exp) -> target' src dest exp 32 | | Let(xt, exp, e) -> 33 | let c1, rs1 = target' src xt exp in 34 | if c1 then true, rs1 else 35 | let c2, rs2 = target src dest e in 36 | c2, rs1 @ rs2 37 | 38 | and target_args src all n = function (* auxiliary function for Call *) 39 | | [] -> [] 40 | | y :: ys when src = y -> all.(n) :: target_args src all (n + 1) ys 41 | | _ :: ys -> target_args src all (n + 1) ys 42 | 43 | type alloc_result = (* allocにおいてspillingがあったかどうかを表すデータ型 *) 44 | | Alloc of Id.t (* allocated register *) 45 | | Spill of Id.t (* spilled variable *) 46 | 47 | let rec alloc dest cont regenv x t = 48 | (* allocate a register or spill a variable *) 49 | assert (not (M.mem x regenv)); 50 | let all = 51 | match t with 52 | | Type.Unit -> ["%r0"] (* dummy *) 53 | | Type.Float -> allfregs 54 | | _ -> allregs in 55 | if all = ["%r0"] then Alloc("%r0") else (* [XX] ad hoc optimization *) 56 | if is_reg x then Alloc(x) else 57 | let free = fv cont in 58 | try 59 | let (c, prefer) = target x dest cont in 60 | let live = (* 生きているレジスタ *) 61 | List.fold_left 62 | (fun live y -> 63 | if is_reg y then S.add y live else 64 | try S.add (M.find y regenv) live 65 | with Not_found -> live) 66 | S.empty 67 | free in 68 | let r = (* そうでないレジスタを探す *) 69 | List.find 70 | (fun r -> not (S.mem r live)) 71 | (prefer @ all) in 72 | (* Format.eprintf "allocated %s to %s@." x r; *) 73 | Alloc(r) 74 | with Not_found -> 75 | Format.eprintf "register allocation failed for %s@." x; 76 | let y = (* 型の合うレジスタ変数を探す *) 77 | List.find 78 | (fun y -> 79 | not (is_reg y) && 80 | try List.mem (M.find y regenv) all 81 | with Not_found -> false) 82 | (List.rev free) in 83 | Format.eprintf "spilling %s from %s@." y (M.find y regenv); 84 | Spill(y) 85 | 86 | (* auxiliary function for g and g'_and_restore *) 87 | let add x r regenv = 88 | if is_reg x then (assert (x = r); regenv) else 89 | M.add x r regenv 90 | 91 | (* auxiliary functions for g' *) 92 | exception NoReg of Id.t * Type.t 93 | 94 | let find x t regenv = 95 | if is_reg x then x else 96 | try M.find x regenv 97 | with Not_found -> raise (NoReg(x, t)) 98 | 99 | let find' x' regenv = 100 | match x' with 101 | | V(x) -> V(find x Type.Int regenv) 102 | | c -> c 103 | 104 | let rec g dest cont regenv = function (* 命令列のレジスタ割り当て (caml2html: regalloc_g) *) 105 | | Ans(exp) -> g'_and_restore dest cont regenv exp 106 | | Let((x, t) as xt, exp, e) -> 107 | assert (not (M.mem x regenv)); 108 | let cont' = concat e dest cont in 109 | let (e1', regenv1) = g'_and_restore xt cont' regenv exp in 110 | (match alloc dest cont' regenv1 x t with 111 | | Spill(y) -> 112 | let r = M.find y regenv1 in 113 | let (e2', regenv2) = g dest cont (add x r (M.remove y regenv1)) e in 114 | let save = 115 | try Save(M.find y regenv, y) 116 | with Not_found -> Nop in 117 | (seq(save, concat e1' (r, t) e2'), regenv2) 118 | | Alloc(r) -> 119 | let (e2', regenv2) = g dest cont (add x r regenv1) e in 120 | (concat e1' (r, t) e2', regenv2)) 121 | 122 | and g'_and_restore dest cont regenv exp = (* 使用される変数をスタックからレジスタへRestore (caml2html: regalloc_unspill) *) 123 | try g' dest cont regenv exp 124 | with NoReg(x, t) -> 125 | ((* Format.eprintf "restoring %s@." x; *) 126 | g dest cont regenv (Let((x, t), Restore(x), Ans(exp)))) 127 | 128 | and g' dest cont regenv = function (* 各命令のレジスタ割り当て (caml2html: regalloc_gprime) *) 129 | | Nop | Li _ | SetL _ | Comment _ | Restore _ | FLi _ as exp -> (Ans(exp), regenv) 130 | | Mov x -> Ans (Mov (find x Type.Int regenv)), regenv 131 | | Mr(x) -> (Ans(Mr(find x Type.Int regenv)), regenv) 132 | | Neg(x) -> (Ans(Neg(find x Type.Int regenv)), regenv) 133 | | Add(x, y') -> (Ans(Add(find x Type.Int regenv, find' y' regenv)), regenv) 134 | | Sub(x, y') -> (Ans(Sub(find x Type.Int regenv, find' y' regenv)), regenv) 135 | | Mul(x, y') -> (Ans(Mul(find x Type.Int regenv, find' y' regenv)), regenv) 136 | | Div(x, y') -> (Ans(Div(find x Type.Int regenv, find' y' regenv)), regenv) 137 | | Mod(x, y') -> (Ans(Mod(find x Type.Int regenv, find' y' regenv)), regenv) 138 | | Lfd(x, y') -> (Ans(Lfd(find x Type.Int regenv, find' y' regenv)), regenv) 139 | | Lwz(x, y') -> (Ans(Lwz(find x Type.Int regenv, find' y' regenv)), regenv) 140 | | Ld(x, y', i) -> (Ans(Ld(find x Type.Int regenv, find' y' regenv, i)), regenv) 141 | | Slw(x, y') -> (Ans(Slw(find x Type.Int regenv, find' y' regenv)), regenv) 142 | | Stw(x, y, z') -> (Ans(Stw(find x Type.Int regenv, find y Type.Int regenv, find' z' regenv)), regenv) 143 | | Stfd(x, y, z') -> (Ans(Stfd(find x Type.Float regenv, find y Type.Int regenv, find' z' regenv)), regenv) 144 | | St(x, y, z', i) -> (Ans(St(find x Type.Int regenv, find y Type.Int regenv, find' z' regenv, i)), regenv) 145 | | FMr(x) -> (Ans(FMr(find x Type.Float regenv)), regenv) 146 | | FMov x -> Ans (FMov (find x Type.Float regenv)), regenv 147 | | FNeg(x) -> (Ans(FNeg(find x Type.Float regenv)), regenv) 148 | | FAdd(x, y) -> (Ans(FAdd(find x Type.Float regenv, find y Type.Float regenv)), regenv) 149 | | FSub(x, y) -> (Ans(FSub(find x Type.Float regenv, find y Type.Float regenv)), regenv) 150 | | FMul(x, y) -> (Ans(FMul(find x Type.Float regenv, find y Type.Float regenv)), regenv) 151 | | FDiv(x, y) -> (Ans(FDiv(find x Type.Float regenv, find y Type.Float regenv)), regenv) 152 | | IfEq(x, y', e1, e2) as exp -> g'_if dest cont regenv exp (fun e1' e2' -> IfEq(find x Type.Int regenv, find' y' regenv, e1', e2')) e1 e2 153 | | IfLE(x, y', e1, e2) as exp -> g'_if dest cont regenv exp (fun e1' e2' -> IfLE(find x Type.Int regenv, find' y' regenv, e1', e2')) e1 e2 154 | | IfGE(x, y', e1, e2) as exp -> g'_if dest cont regenv exp (fun e1' e2' -> IfGE(find x Type.Int regenv, find' y' regenv, e1', e2')) e1 e2 155 | | IfFEq(x, y, e1, e2) as exp -> g'_if dest cont regenv exp (fun e1' e2' -> IfFEq(find x Type.Float regenv, find y Type.Float regenv, e1', e2')) e1 e2 156 | | IfFLE(x, y, e1, e2) as exp -> g'_if dest cont regenv exp (fun e1' e2' -> IfFLE(find x Type.Float regenv, find y Type.Float regenv, e1', e2')) e1 e2 157 | | CallCls(x, ys, zs) as exp -> 158 | if List.length ys > Array.length regs - 2 || List.length zs > Array.length fregs - 1 then 159 | failwith (Format.sprintf "cannot allocate registers for arugments to %s" x) 160 | else g'_call dest cont regenv exp (fun ys zs -> CallCls(find x Type.Int regenv, ys, zs)) ys zs 161 | | CallDir(Id.L(x), ys, zs) as exp -> 162 | if List.length ys > Array.length regs - 1 || List.length zs > Array.length fregs - 1 then 163 | failwith (Format.sprintf "cannot allocate registers for arugments to %s" x) 164 | else g'_call dest cont regenv exp (fun ys zs -> CallDir(Id.L(x), ys, zs)) ys zs 165 | | Save(x, y) -> assert false 166 | | _ as exp -> (Ans(exp), regenv) 167 | 168 | and g'_if dest cont regenv exp constr e1 e2 = (* ifのレジスタ割り当て (caml2html: regalloc_if) *) 169 | let (e1', regenv1) = g dest cont regenv e1 in 170 | let (e2', regenv2) = g dest cont regenv e2 in 171 | let regenv' = (* 両方に共通のレジスタ変数だけ利用 *) 172 | List.fold_left 173 | (fun regenv' x -> 174 | try 175 | if is_reg x then regenv' else 176 | let r1 = M.find x regenv1 in 177 | let r2 = M.find x regenv2 in 178 | if r1 <> r2 then regenv' else 179 | M.add x r1 regenv' 180 | with Not_found -> regenv') 181 | M.empty 182 | (fv cont) in 183 | (List.fold_left 184 | (fun e x -> 185 | if x = fst dest || not (M.mem x regenv) || M.mem x regenv' then e else 186 | seq(Save(M.find x regenv, x), e)) (* そうでない変数は分岐直前にセーブ *) 187 | (Ans(constr e1' e2')) 188 | (fv cont), 189 | regenv') 190 | 191 | and g'_call dest cont regenv exp constr ys zs = (* 関数呼び出しのレジスタ割り当て (caml2html: regalloc_call) *) 192 | (List.fold_left 193 | (fun e x -> 194 | if x = fst dest || not (M.mem x regenv) then e else 195 | seq(Save(M.find x regenv, x), e)) 196 | (Ans(constr 197 | (List.map (fun y -> find y Type.Int regenv) ys) 198 | (List.map (fun z -> find z Type.Float regenv) zs))) 199 | (fv cont), 200 | M.empty) 201 | 202 | let h { name = Id.L(x); args = ys; fargs = zs; body = e; ret = t ; annot } = (* 関数のレジスタ割り当て (caml2html: regalloc_h) *) 203 | let regenv = M.add x reg_cl M.empty in 204 | let (i, arg_regs, regenv) = 205 | List.fold_left 206 | (fun (i, arg_regs, regenv) y -> 207 | let r = regs.(i) in 208 | (i + 1, 209 | arg_regs @ [r], 210 | (assert (not (is_reg y)); 211 | M.add y r regenv))) 212 | (0, [], regenv) 213 | ys in 214 | let (d, farg_regs, regenv) = 215 | List.fold_left 216 | (fun (d, farg_regs, regenv) z -> 217 | let fr = fregs.(d) in 218 | (d + 1, 219 | farg_regs @ [fr], 220 | (assert (not (is_reg z)); 221 | M.add z fr regenv))) 222 | (0, [], regenv) 223 | zs in 224 | let a = 225 | match t with 226 | | Type.Unit -> Id.gentmp Type.Unit 227 | | Type.Float -> fregs.(0) 228 | | _ -> regs.(0) in 229 | let (e', regenv') = g (a, t) (Ans(Mr(a))) regenv e in 230 | { name = Id.L(x); args = arg_regs; fargs = farg_regs; body = e'; ret = t ; annot } 231 | 232 | let f (Prog(data, fundefs, e)) = (* プログラム全体のレジスタ割り当て (caml2html: regalloc_f) *) 233 | Format.eprintf "register allocation: may take some time (up to a few minutes, depending on the size of functions)@."; 234 | let fundefs' = List.map h fundefs in 235 | let e', regenv' = g (Id.gentmp Type.Unit, Type.Unit) (Ans(Nop)) M.empty e in 236 | Prog(data, fundefs', e') 237 | -------------------------------------------------------------------------------- /src/arm64/regAlloc.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | 3 | val f : Asm.prog -> Asm.prog 4 | -------------------------------------------------------------------------------- /src/arm64/stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void min_caml_start(char *, char *); 5 | 6 | /* "stderr" is a macro and cannot be referred to in libmincaml.S, so 7 | this "min_caml_stderr" is used (in place of "__iob+32") for better 8 | portability (under SPARC emulators, for example). Thanks to Steven 9 | Shaw for reporting the problem and proposing this solution. */ 10 | FILE *min_caml_stderr; 11 | 12 | int main() { 13 | char *hp, *sp; 14 | 15 | min_caml_stderr = stderr; 16 | sp = alloca(1000000); hp = malloc(4000000); 17 | if (hp == NULL || sp == NULL) { 18 | fprintf(stderr, "malloc or alloca failed\n"); 19 | return 1; 20 | } 21 | // fprintf(stderr, "sp = %p, hp = %p\n", sp, hp); 22 | min_caml_start(sp, hp); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/joe/alpha.ml: -------------------------------------------------------------------------------- 1 | (* rename identifiers to make them unique (alpha-conversion) *) 2 | 3 | open KNormal 4 | 5 | let find x env = try M.find x env with Not_found -> x 6 | 7 | let rec g env = function (* α変換ルーチン本体 (caml2html: alpha_g) *) 8 | | Unit -> Unit 9 | | Int i -> Int i 10 | | Float d -> Float d 11 | | Neg x -> Neg (find x env) 12 | | Add (x, y) -> Add (find x env, find y env) 13 | | Sub (x, y) -> Sub (find x env, find y env) 14 | | Mul (x, y) -> Mul (find x env, find y env) 15 | | Div (x, y) -> Div (find x env, find y env) 16 | | Mod (x, y) -> Mod (find x env, find y env) 17 | | FNeg x -> FNeg (find x env) 18 | | FAdd (x, y) -> FAdd (find x env, find y env) 19 | | FSub (x, y) -> FSub (find x env, find y env) 20 | | FMul (x, y) -> FMul (find x env, find y env) 21 | | FDiv (x, y) -> FDiv (find x env, find y env) 22 | | IfEq (x, y, e1, e2) -> IfEq (find x env, find y env, g env e1, g env e2) 23 | | IfLE (x, y, e1, e2) -> IfLE (find x env, find y env, g env e1, g env e2) 24 | | Let ((x, t), e1, e2) -> 25 | let x' = Id.genid x in (* letのα変換 (caml2html: alpha_let) *) 26 | Let ((x', t), g env e1, g (M.add x x' env) e2) 27 | | Var x -> Var (find x env) 28 | | LetRec ({ name = x, t; args = yts; body = e1; annot }, e2) -> (* let recのα変換 (caml2html: alpha_letrec) *) 29 | let env = M.add x (Id.genid x) env in 30 | let ys = List.map fst yts in 31 | let env' = M.add_list2 ys (List.map Id.genid ys) env in 32 | LetRec 33 | ( { name = find x env, t 34 | ; args = List.map (fun (y, t) -> find y env', t) yts 35 | ; body = g env' e1 36 | ; annot 37 | } 38 | , g env e2 ) 39 | | App (x, ys) -> App (find x env, List.map (fun y -> find y env) ys) 40 | | Tuple xs -> Tuple (List.map (fun x -> find x env) xs) 41 | | LetTuple (xts, y, e) -> 42 | let xs = List.map fst xts in 43 | let env' = M.add_list2 xs (List.map Id.genid xs) env in 44 | LetTuple (List.map (fun (x, t) -> find x env', t) xts, find y env, g env' e) 45 | | Get (x, y) -> Get (find x env, find y env) 46 | | Put (x, y, z) -> Put (find x env, find y env, find z env) 47 | | ExtArray x -> ExtArray x 48 | | ExtFunApp (x, ys) -> ExtFunApp (x, List.map (fun y -> find y env) ys) 49 | 50 | let f = g M.empty 51 | -------------------------------------------------------------------------------- /src/joe/alpha.mli: -------------------------------------------------------------------------------- 1 | val f : KNormal.t -> KNormal.t 2 | val g : Id.t M.t -> KNormal.t -> KNormal.t (* for Inline.g *) 3 | -------------------------------------------------------------------------------- /src/joe/asm.ml: -------------------------------------------------------------------------------- 1 | type id_or_imm = 2 | | V of Id.t 3 | | C of int 4 | [@@deriving show] 5 | 6 | type t = 7 | | Ans of exp 8 | | Let of (Id.t * Type.t) * exp * t 9 | [@@deriving show] 10 | 11 | and exp = 12 | | Nop 13 | | Li of int 14 | | FLi of Id.l 15 | | SetL of Id.l 16 | | Mr of Id.t 17 | | Mov of Id.t 18 | (* ALU instructions *) 19 | | Neg of Id.t 20 | | Add of Id.t * id_or_imm 21 | | Sub of Id.t * id_or_imm 22 | | Mul of Id.t * id_or_imm 23 | | Div of Id.t * id_or_imm 24 | | Mod of Id.t * id_or_imm 25 | (* load/store instructions *) 26 | | Lfd of Id.t * id_or_imm 27 | | Lwz of Id.t * id_or_imm 28 | | Ld of Id.t * id_or_imm * int 29 | | Slw of Id.t * id_or_imm 30 | | Stw of Id.t * Id.t * id_or_imm 31 | | Stfd of Id.t * Id.t * id_or_imm 32 | | St of Id.t * Id.t * id_or_imm * int 33 | (* float instructions *) 34 | | FMov of Id.t 35 | | FNeg of Id.t 36 | | FAdd of Id.t * Id.t 37 | | FSub of Id.t * Id.t 38 | | FMul of Id.t * Id.t 39 | | FDiv of Id.t * Id.t 40 | | LdDF of Id.t * id_or_imm * int 41 | | StDF of Id.t * Id.t * id_or_imm * int 42 | | FMr of Id.t 43 | | Comment of string 44 | (* virtual instructions *) 45 | | IfEq of Id.t * id_or_imm * t * t 46 | | IfLE of Id.t * id_or_imm * t * t 47 | | IfGE of Id.t * id_or_imm * t * t (* 左右対称ではないので必要 *) 48 | | IfFEq of Id.t * Id.t * t * t 49 | | IfFLE of Id.t * Id.t * t * t 50 | (* closure address, integer arguments, and float arguments *) 51 | | CallCls of Id.t * Id.t list * Id.t list 52 | | CallDir of Id.l * Id.t list * Id.t list 53 | | Save of Id.t * Id.t (* レジスタ変数の値をスタック変数へ保存 (caml2html: sparcasm_save) *) 54 | | Restore of Id.t (* スタック変数から値を復元 (caml2html: sparcasm_restore) *) 55 | [@@deriving show] 56 | 57 | type fundef = 58 | { name : Id.l 59 | ; args : Id.t list 60 | ; fargs : Id.t list 61 | ; body : t 62 | ; ret : Type.t 63 | ; mutable annot : [ `TJ | `MJ ] option 64 | } 65 | [@@deriving show] 66 | 67 | (* プログラム全体 = 浮動小数点数テーブル + トップレベル関数 + メインの式 (caml2html: sparcasm_prog) *) 68 | type prog = Prog of (Id.l * float) list * fundef list * t [@@deriving show] 69 | 70 | let fletd (x, e1, e2) = Let ((x, Type.Float), e1, e2) 71 | let seq (e1, e2) = Let ((Id.gentmp Type.Unit, Type.Unit), e1, e2) 72 | 73 | module X86 = struct 74 | let regs = (* Array.init 16 (fun i -> Printf.sprintf "%%r%d" i) *) [| "%eax"; "%ebx"; "%ecx"; "%edx"; "%esi"; "%edi" |] 75 | let fregs = Array.init 8 (fun i -> Printf.sprintf "%%xmm%d" i) 76 | let allregs = Array.to_list regs 77 | let allfregs = Array.to_list fregs 78 | let reg_cl = regs.(Array.length regs - 1) 79 | (* closure address (caml2html: sparcasm_regcl) *) 80 | (* let reg_sw = regs.(Array.length regs - 1) (* temporary for swap *) 81 | let reg_fsw = fregs.(Array.length fregs - 1) (* temporary for swap *) *) 82 | let reg_sp = "%ebp" (* stack pointer *) 83 | let reg_hp = "min_caml_hp" (* heap pointer (caml2html: sparcasm_reghp) *) 84 | (* let reg_ra = "%eax" (* return address *) *) 85 | let is_reg x = x.[0] = '%' || x = reg_hp 86 | end 87 | 88 | module X64 = struct 89 | let regs = (* Array.init 16 (fun i -> Printf.sprintf "%%r%d" i) *) [| "%rax"; "%rbx"; "%rcx"; "%rdx"; "%rsi"; "%rdi" |] 90 | let fregs = Array.init 8 (fun i -> Printf.sprintf "%%xmm%d" i) 91 | let allregs = Array.to_list regs 92 | let allfregs = Array.to_list fregs 93 | let reg_cl = regs.(Array.length regs - 1) 94 | (* closure address (caml2html: sparcasm_regcl) *) 95 | let reg_sp = "%rbp" (* stack pointer *) 96 | let reg_hp = "min_caml_hp" (* heap pointer (caml2html: sparcasm_reghp) *) 97 | let is_reg x = x.[0] = '%' || x = reg_hp 98 | end 99 | 100 | module ARM64 = struct 101 | let regs = (* Array.init 27 (fun i -> Printf.sprintf "_R_%d" i) *) 102 | [| "%x0"; "%x1"; "%x2"; "%x3"; "%x4"; "%x5"; "%x6"; "%x7"; "%x8"; "%x9"; "%x10"; 103 | "%x11"; "%x12"; "%x13"; "%x14"; "%x15"; "%x16"; "%x17"; "%x18"; "%x19"; "%x20"; 104 | "%x21"; "%x22"; "%x23"; "%x24"; "%x25" |] 105 | let fregs = Array.init 32 (fun i -> Printf.sprintf "%%d%d" i) 106 | let allregs = Array.to_list regs 107 | let allfregs = Array.to_list fregs 108 | let reg_cl = regs.(Array.length regs - 1) (* closure address (caml2html: sparcasm_regcl) *) 109 | let reg_sw = regs.(Array.length regs - 2) (* temporary for swap *) 110 | let reg_fsw = fregs.(Array.length fregs - 1) (* temporary for swap *) 111 | let reg_sp = "%x28" (* stack pointer *) 112 | let reg_hp = "%x27" (* heap pointer (caml2html: sparcasm_reghp) *) 113 | let reg_tmp = "%x26" (* [XX] ad hoc *) 114 | let is_reg x = (x.[0] = '%') 115 | end 116 | 117 | (* super-tenuki *) 118 | let rec remove_and_uniq xs = function 119 | | [] -> [] 120 | | x :: ys when S.mem x xs -> remove_and_uniq xs ys 121 | | x :: ys -> x :: remove_and_uniq (S.add x xs) ys 122 | 123 | (* free variables in the order of use (for spilling) (caml2html: sparcasm_fv) *) 124 | let fv_id_or_imm = function V x -> [ x ] | _ -> [] 125 | 126 | let rec fv_exp = function 127 | | Nop | SetL _ | Comment _ | Restore _ -> [] 128 | | Mov x | Neg x | FMov x | FNeg x | Save (x, _) -> [ x ] 129 | | Add (x, y') | Mul (x, y') | Sub (x, y') | Div (x, y') | Mod (x, y') 130 | | Ld (x, y', _) | LdDF (x, y', _) -> x :: fv_id_or_imm y' 131 | | St (x, y, z', _) | StDF (x, y, z', _) -> x :: y :: fv_id_or_imm z' 132 | | Slw(x, y') | Lfd(x, y') | Lwz(x, y') -> x :: fv_id_or_imm y' 133 | | Stw(x, y, z') | Stfd(x, y, z') -> x :: y :: fv_id_or_imm z' 134 | | FAdd (x, y) | FSub (x, y) | FMul (x, y) | FDiv (x, y) -> [ x; y ] 135 | | IfEq (x, y', e1, e2) | IfLE (x, y', e1, e2) | IfGE (x, y', e1, e2) -> (x :: fv_id_or_imm y') @ remove_and_uniq S.empty (fv e1 @ fv e2) 136 | (* uniq here just for efficiency *) 137 | | IfFEq (x, y, e1, e2) | IfFLE (x, y, e1, e2) -> x :: y :: remove_and_uniq S.empty (fv e1 @ fv e2) 138 | (* uniq here just for efficiency *) 139 | | CallCls (x, ys, zs) -> (x :: ys) @ zs 140 | | CallDir (_, ys, zs) -> ys @ zs 141 | | _ -> [] 142 | 143 | and fv = function 144 | | Ans exp -> fv_exp exp 145 | | Let ((x, t), exp, e) -> fv_exp exp @ remove_and_uniq (S.singleton x) (fv e) 146 | 147 | let fv e = remove_and_uniq S.empty (fv e) 148 | 149 | let rec concat e1 xt e2 = 150 | match e1 with 151 | | Ans exp -> Let (xt, exp, e2) 152 | | Let (yt, exp, e1') -> Let (yt, exp, concat e1' xt e2) 153 | 154 | let align i = if i mod 8 = 0 then i else i + 4 155 | 156 | (* all local variables *) 157 | let rec localvs_exp = function 158 | | IfEq (_, _, e1, e2) 159 | | IfLE (_, _, e1, e2) 160 | | IfGE (_, _, e1, e2) 161 | | IfFEq (_, _, e1, e2) 162 | | IfFLE (_, _, e1, e2) -> 163 | localvs e1 @ localvs e2 164 | | _ -> [] 165 | 166 | and localvs = function 167 | | Ans exp -> localvs_exp exp 168 | | Let (xt, exp, e) -> (xt :: localvs_exp exp) @ localvs e 169 | -------------------------------------------------------------------------------- /src/joe/asm.mli: -------------------------------------------------------------------------------- 1 | type id_or_imm = 2 | | V of Id.t 3 | | C of int 4 | 5 | type t = 6 | | Ans of exp 7 | | Let of (Id.t * Type.t) * exp * t 8 | 9 | and exp = 10 | | Nop 11 | | Li of int 12 | | FLi of Id.l 13 | | SetL of Id.l 14 | | Mr of Id.t 15 | | Mov of Id.t 16 | (* ALU instructions *) 17 | | Neg of Id.t 18 | | Add of Id.t * id_or_imm 19 | | Sub of Id.t * id_or_imm 20 | | Mul of Id.t * id_or_imm 21 | | Div of Id.t * id_or_imm 22 | | Mod of Id.t * id_or_imm 23 | (* load/store instructions *) 24 | | Lfd of Id.t * id_or_imm 25 | | Lwz of Id.t * id_or_imm 26 | | Ld of Id.t * id_or_imm * int 27 | | Slw of Id.t * id_or_imm 28 | | Stw of Id.t * Id.t * id_or_imm 29 | | Stfd of Id.t * Id.t * id_or_imm 30 | | St of Id.t * Id.t * id_or_imm * int 31 | (* float instructions *) 32 | | FMov of Id.t 33 | | FNeg of Id.t 34 | | FAdd of Id.t * Id.t 35 | | FSub of Id.t * Id.t 36 | | FMul of Id.t * Id.t 37 | | FDiv of Id.t * Id.t 38 | | LdDF of Id.t * id_or_imm * int 39 | | StDF of Id.t * Id.t * id_or_imm * int 40 | | FMr of Id.t 41 | | Comment of string 42 | (* virtual instructions *) 43 | | IfEq of Id.t * id_or_imm * t * t 44 | | IfLE of Id.t * id_or_imm * t * t 45 | | IfGE of Id.t * id_or_imm * t * t (* 左右対称ではないので必要 *) 46 | | IfFEq of Id.t * Id.t * t * t 47 | | IfFLE of Id.t * Id.t * t * t 48 | (* closure address, integer arguments, and float arguments *) 49 | | CallCls of Id.t * Id.t list * Id.t list 50 | | CallDir of Id.l * Id.t list * Id.t list 51 | | Save of Id.t * Id.t (* レジスタ変数の値をスタック変数へ保存 (caml2html: sparcasm_save) *) 52 | | Restore of Id.t (* スタック変数から値を復元 (caml2html: sparcasm_restore) *) 53 | 54 | type fundef = 55 | { name : Id.l 56 | ; args : Id.t list 57 | ; fargs : Id.t list 58 | ; body : t 59 | ; ret : Type.t 60 | ; mutable annot : [ `TJ | `MJ ] option 61 | } 62 | 63 | type prog = Prog of (Id.l * float) list * fundef list * t 64 | 65 | val show : t -> string 66 | val show_exp : exp -> string 67 | val show_fundef : fundef -> string 68 | val show_prog : prog -> string 69 | val fletd : Id.t * exp * t -> t (* shorthand of Let for float *) 70 | 71 | val seq : exp * t -> t (* shorthand of Let for unit *) 72 | 73 | module X86 : sig 74 | val regs : Id.t array 75 | val fregs : Id.t array 76 | val allregs : Id.t list 77 | val allfregs : Id.t list 78 | val reg_cl : Id.t 79 | (* val reg_sw : Id.t 80 | val reg_fsw : Id.t 81 | val reg_ra : Id.t *) 82 | val reg_hp : Id.t 83 | val reg_sp : Id.t 84 | val is_reg : Id.t -> bool 85 | end 86 | 87 | module X64 : sig 88 | val regs : Id.t array 89 | val fregs : Id.t array 90 | val allregs : Id.t list 91 | val allfregs : Id.t list 92 | val reg_cl : Id.t 93 | val reg_hp : Id.t 94 | val reg_sp : Id.t 95 | val is_reg : Id.t -> bool 96 | end 97 | 98 | module ARM64 : sig 99 | val regs : Id.t array 100 | val fregs : Id.t array 101 | val allregs : Id.t list 102 | val allfregs : Id.t list 103 | val reg_cl : Id.t 104 | val reg_sw : Id.t 105 | val reg_fsw : Id.t 106 | val reg_sp : Id.t 107 | val reg_hp : Id.t 108 | val reg_tmp : Id.t 109 | val is_reg : Id.t -> bool 110 | end 111 | 112 | val fv : t -> Id.t list 113 | val concat : t -> Id.t * Type.t -> t -> t 114 | val align : int -> int 115 | val localvs : t -> (Id.t * Type.t) list 116 | -------------------------------------------------------------------------------- /src/joe/assoc.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/joe/assoc.ml -------------------------------------------------------------------------------- /src/joe/assoc.mli: -------------------------------------------------------------------------------- 1 | val f : KNormal.t -> KNormal.t 2 | -------------------------------------------------------------------------------- /src/joe/beta.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/joe/beta.ml -------------------------------------------------------------------------------- /src/joe/beta.mli: -------------------------------------------------------------------------------- 1 | val f : KNormal.t -> KNormal.t 2 | -------------------------------------------------------------------------------- /src/joe/closure.ml: -------------------------------------------------------------------------------- 1 | type closure = 2 | { entry : Id.l 3 | ; actual_fv : Id.t list 4 | } 5 | 6 | type t = (* クロージャ変換後の式 (caml2html: closure_t) *) 7 | | Unit 8 | | Int of int 9 | | Float of float 10 | | Neg of Id.t 11 | | Add of Id.t * Id.t 12 | | Sub of Id.t * Id.t 13 | | Mul of Id.t * Id.t 14 | | Div of Id.t * Id.t 15 | | Mod of Id.t * Id.t 16 | | FNeg of Id.t 17 | | FAdd of Id.t * Id.t 18 | | FSub of Id.t * Id.t 19 | | FMul of Id.t * Id.t 20 | | FDiv of Id.t * Id.t 21 | | IfEq of Id.t * Id.t * t * t 22 | | IfLE of Id.t * Id.t * t * t 23 | | Let of (Id.t * Type.t) * t * t 24 | | Var of Id.t 25 | | MakeCls of (Id.t * Type.t) * closure * t 26 | | AppCls of Id.t * Id.t list 27 | | AppDir of Id.l * Id.t list 28 | | Tuple of Id.t list 29 | | LetTuple of (Id.t * Type.t) list * Id.t * t 30 | | Get of Id.t * Id.t 31 | | Put of Id.t * Id.t * Id.t 32 | | ExtArray of Id.l 33 | 34 | type fundef = 35 | { name : Id.l * Type.t 36 | ; args : (Id.t * Type.t) list 37 | ; formal_fv : (Id.t * Type.t) list 38 | ; body : t 39 | ; mutable annot : [ `TJ | `MJ ] option 40 | } 41 | 42 | type prog = Prog of fundef list * t 43 | 44 | let rec fv = function 45 | | Unit | Int _ | Float _ | ExtArray _ -> S.empty 46 | | Neg x | FNeg x -> S.singleton x 47 | | Add (x, y) 48 | | Sub (x, y) 49 | | Mul (x, y) 50 | | Div (x, y) 51 | | Mod (x, y) 52 | | FAdd (x, y) 53 | | FSub (x, y) 54 | | FMul (x, y) 55 | | FDiv (x, y) 56 | | Get (x, y) -> S.of_list [ x; y ] 57 | | IfEq (x, y, e1, e2) | IfLE (x, y, e1, e2) -> S.add x (S.add y (S.union (fv e1) (fv e2))) 58 | | Let ((x, t), e1, e2) -> S.union (fv e1) (S.remove x (fv e2)) 59 | | Var x -> S.singleton x 60 | | MakeCls ((x, t), { entry = l; actual_fv = ys }, e) -> S.remove x (S.union (S.of_list ys) (fv e)) 61 | | AppCls (x, ys) -> S.of_list (x :: ys) 62 | | AppDir (_, xs) | Tuple xs -> S.of_list xs 63 | | LetTuple (xts, y, e) -> S.add y (S.diff (fv e) (S.of_list (List.map fst xts))) 64 | | Put (x, y, z) -> S.of_list [ x; y; z ] 65 | 66 | let toplevel : fundef list ref = ref [] 67 | 68 | let rec g env known = function (* クロージャ変換ルーチン本体 (caml2html: closure_g) *) 69 | | KNormal.Unit -> Unit 70 | | KNormal.Int i -> Int i 71 | | KNormal.Float d -> Float d 72 | | KNormal.Neg x -> Neg x 73 | | KNormal.Add (x, y) -> Add (x, y) 74 | | KNormal.Sub (x, y) -> Sub (x, y) 75 | | KNormal.Mul (x, y) -> Mul (x, y) 76 | | KNormal.Div (x, y) -> Div (x, y) 77 | | KNormal.Mod (x, y) -> Mod (x, y) 78 | | KNormal.FNeg x -> FNeg x 79 | | KNormal.FAdd (x, y) -> FAdd (x, y) 80 | | KNormal.FSub (x, y) -> FSub (x, y) 81 | | KNormal.FMul (x, y) -> FMul (x, y) 82 | | KNormal.FDiv (x, y) -> FDiv (x, y) 83 | | KNormal.IfEq (x, y, e1, e2) -> IfEq (x, y, g env known e1, g env known e2) 84 | | KNormal.IfLE (x, y, e1, e2) -> IfLE (x, y, g env known e1, g env known e2) 85 | | KNormal.Let ((x, t), e1, e2) -> Let ((x, t), g env known e1, g (M.add x t env) known e2) 86 | | KNormal.Var x -> Var x 87 | | KNormal.LetRec ({ KNormal.name = x, t; KNormal.args = yts; KNormal.body = e1; annot }, e2) -> 88 | (* 関数定義の場合 (caml2html: closure_letrec) *) 89 | (* 関数定義let rec x y1 ... yn = e1 in e2の場合は、 90 | xに自由変数がない(closureを介さずdirectに呼び出せる) 91 | と仮定し、knownに追加してe1をクロージャ変換してみる *) 92 | let toplevel_backup = !toplevel in 93 | let env' = M.add x t env in 94 | let known' = S.add x known in 95 | let e1' = g (M.add_list yts env') known' e1 in 96 | (* 本当に自由変数がなかったか、変換結果e1'を確認する *) 97 | (* 注意: e1'にx自身が変数として出現する場合はclosureが必要! 98 | (thanks to nuevo-namasute and azounoman; test/cls-bug2.ml参照) *) 99 | let zs = S.diff (fv e1') (S.of_list (List.map fst yts)) in 100 | let known', e1' = if S.is_empty zs then known', e1' else 101 | (* 駄目だったら状態(toplevelの値)を戻して、クロージャ変換をやり直す *) 102 | ( Format.eprintf "free variable(s) %s found in function %s@." (Id.pp_list (S.elements zs)) x; 103 | Format.eprintf "function %s cannot be directly applied in fact@." x; 104 | toplevel := toplevel_backup; let e1' = g (M.add_list yts env') known e1 in known, e1' ) in 105 | let zs = S.elements (S.diff (fv e1') (S.add x (S.of_list (List.map fst yts)))) in (* 自由変数のリスト *) 106 | let zts = List.map (fun z -> z, M.find z env') zs in (* ここで自由変数zの型を引くために引数envが必要 *) 107 | toplevel := { name = Id.L x, t; args = yts; formal_fv = zts; body = e1'; annot } :: !toplevel; (* トップレベル関数を追加 *) 108 | let e2' = g env' known' e2 in 109 | if S.mem x (fv e2') then (* xが変数としてe2'に出現するか *) 110 | MakeCls ((x, t), { entry = Id.L x; actual_fv = zs }, e2') (* 出現していたら削除しない *) 111 | else (Format.eprintf "eliminating closure(s) %s@." x; e2') (* 出現しなければMakeClsを削除 *) 112 | | KNormal.App (x, ys) when S.mem x known -> (* 関数適用の場合 (caml2html: closure_app) *) 113 | Format.eprintf "directly applying %s@." x; AppDir (Id.L x, ys) 114 | | KNormal.App (f, xs) -> AppCls (f, xs) 115 | | KNormal.Tuple xs -> Tuple xs 116 | | KNormal.LetTuple (xts, y, e) -> LetTuple (xts, y, g (M.add_list xts env) known e) 117 | | KNormal.Get (x, y) -> Get (x, y) 118 | | KNormal.Put (x, y, z) -> Put (x, y, z) 119 | | KNormal.ExtArray x -> ExtArray (Id.L x) 120 | | KNormal.ExtFunApp (x, ys) -> AppDir (Id.L ("min_caml_" ^ x), ys) 121 | 122 | let f e = 123 | toplevel := []; 124 | let e' = g M.empty S.empty e in 125 | Prog (List.rev !toplevel, e') 126 | -------------------------------------------------------------------------------- /src/joe/closure.mli: -------------------------------------------------------------------------------- 1 | type closure = 2 | { entry : Id.l 3 | ; actual_fv : Id.t list 4 | } 5 | 6 | type t = 7 | | Unit 8 | | Int of int 9 | | Float of float 10 | | Neg of Id.t 11 | | Add of Id.t * Id.t 12 | | Sub of Id.t * Id.t 13 | | Mul of Id.t * Id.t 14 | | Div of Id.t * Id.t 15 | | Mod of Id.t * Id.t 16 | | FNeg of Id.t 17 | | FAdd of Id.t * Id.t 18 | | FSub of Id.t * Id.t 19 | | FMul of Id.t * Id.t 20 | | FDiv of Id.t * Id.t 21 | | IfEq of Id.t * Id.t * t * t 22 | | IfLE of Id.t * Id.t * t * t 23 | | Let of (Id.t * Type.t) * t * t 24 | | Var of Id.t 25 | | MakeCls of (Id.t * Type.t) * closure * t 26 | | AppCls of Id.t * Id.t list 27 | | AppDir of Id.l * Id.t list 28 | | Tuple of Id.t list 29 | | LetTuple of (Id.t * Type.t) list * Id.t * t 30 | | Get of Id.t * Id.t 31 | | Put of Id.t * Id.t * Id.t 32 | | ExtArray of Id.l 33 | 34 | type fundef = 35 | { name : Id.l * Type.t 36 | ; args : (Id.t * Type.t) list 37 | ; formal_fv : (Id.t * Type.t) list 38 | ; body : t 39 | ; mutable annot : [ `TJ | `MJ ] option 40 | } 41 | 42 | type prog = Prog of fundef list * t 43 | 44 | val fv : t -> S.t 45 | val f : KNormal.t -> prog 46 | -------------------------------------------------------------------------------- /src/joe/constFold.ml: -------------------------------------------------------------------------------- 1 | open KNormal 2 | 3 | let memi x env = try match M.find x env with Int _ -> true | _ -> false with | Not_found -> false 4 | let memf x env = try match M.find x env with Float _ -> true | _ -> false with | Not_found -> false 5 | let memt x env = try match M.find x env with Tuple _ -> true | _ -> false with | Not_found -> false 6 | 7 | let findi x env = match M.find x env with Int i -> i | _ -> raise Not_found 8 | let findf x env = match M.find x env with Float d -> d | _ -> raise Not_found 9 | let findt x env = match M.find x env with Tuple ys -> ys | _ -> raise Not_found 10 | 11 | let rec g env = function (* 定数畳み込みルーチン本体 (caml2html: constfold_g) *) 12 | | Var x when memi x env -> Int (findi x env) 13 | | Var(x) when memf x env -> Float(findf x env) 14 | | Var(x) when memt x env -> Tuple(findt x env) 15 | | Neg x when memi x env -> Int (-findi x env) 16 | | Add (x, y) when memi x env && memi y env -> Int (findi x env + findi y env) (* 足し算のケース (caml2html: constfold_add) *) 17 | | Sub (x, y) when memi x env && memi y env -> Int (findi x env - findi y env) 18 | | Mul (x, y) when memi x env && memi y env -> Int (findi x env * findi y env) 19 | | Mod (x, y) when memi x env && memi y env -> Int (findi x env mod findi y env) 20 | | FNeg x when memf x env -> Float (-.findf x env) 21 | | FAdd (x, y) when memf x env && memf y env -> Float (findf x env +. findf y env) 22 | | FSub (x, y) when memf x env && memf y env -> Float (findf x env -. findf y env) 23 | | FMul (x, y) when memf x env && memf y env -> Float (findf x env *. findf y env) 24 | | FDiv (x, y) when memf x env && memf y env -> Float (findf x env /. findf y env) 25 | | IfEq (x, y, e1, e2) when memi x env && memi y env -> if findi x env = findi y env then g env e1 else g env e2 26 | | IfEq (x, y, e1, e2) when memf x env && memf y env -> if findf x env = findf y env then g env e1 else g env e2 27 | | IfEq (x, y, e1, e2) -> IfEq (x, y, g env e1, g env e2) 28 | | IfLE (x, y, e1, e2) when memi x env && memi y env -> if findi x env <= findi y env then g env e1 else g env e2 29 | | IfLE (x, y, e1, e2) when memf x env && memf y env -> if findf x env <= findf y env then g env e1 else g env e2 30 | | IfLE (x, y, e1, e2) -> IfLE (x, y, g env e1, g env e2) 31 | | Let ((x, t), e1, e2) -> let e1' = g env e1 in let e2' = g (M.add x e1' env) e2 in Let ((x, t), e1', e2') (* letのケース (caml2html: constfold_let) *) 32 | | LetRec ({ name = x; args = ys; body = e1; annot }, e2) -> LetRec ({ name = x; args = ys; body = g env e1; annot }, g env e2) 33 | | LetTuple (xts, y, e) when memt y env -> List.fold_left2 (fun e' xt z -> Let (xt, Var z, e')) (g env e) xts (findt y env) 34 | | LetTuple (xts, y, e) -> LetTuple (xts, y, g env e) 35 | | e -> e 36 | 37 | let f = g M.empty 38 | -------------------------------------------------------------------------------- /src/joe/constFold.mli: -------------------------------------------------------------------------------- 1 | val f : KNormal.t -> KNormal.t 2 | -------------------------------------------------------------------------------- /src/joe/dune: -------------------------------------------------------------------------------- 1 | (ocamllex (modules lexer)) 2 | (ocamlyacc (modules parser)) 3 | 4 | (library 5 | (name MinCaml) 6 | (public_name joe.lib) 7 | (modules (:standard \ main)) 8 | (flags (-w -4-33-40-41)) 9 | (libraries str) 10 | (preprocess (pps ppx_deriving.show))) 11 | 12 | (executable 13 | (name main) 14 | (public_name joe) 15 | (flags (-w -4-33-40-41)) 16 | (modules Main) 17 | (package joe) 18 | (libraries MinCaml BacCaml x64 arm64)) 19 | -------------------------------------------------------------------------------- /src/joe/elim.ml: -------------------------------------------------------------------------------- 1 | open KNormal 2 | 3 | let rec effect = function (* 副作用の有無 (caml2html: elim_effect) *) 4 | | Let(_, e1, e2) | IfEq(_, _, e1, e2) | IfLE(_, _, e1, e2) -> effect e1 || effect e2 5 | | LetRec(_, e) | LetTuple(_, _, e) -> effect e 6 | | App _ | Put _ | ExtFunApp _ -> true 7 | | _ -> false 8 | 9 | let rec f = function (* 不要定義削除ルーチン本体 (caml2html: elim_f) *) 10 | | IfEq (x, y, e1, e2) -> IfEq (x, y, f e1, f e2) 11 | | IfLE (x, y, e1, e2) -> IfLE (x, y, f e1, f e2) 12 | | Let ((x, t), e1, e2) -> (* letの場合 (caml2html: elim_let) *) 13 | let e1' = f e1 in 14 | let e2' = f e2 in 15 | if effect e1' || S.mem x (fv e2') 16 | then Let ((x, t), e1', e2') 17 | else ( Format.eprintf "eliminating variable %s@." x; e2') 18 | | LetRec ({ name = x, t; args = yts; body = e1; annot }, e2) -> (* let recの場合 (caml2html: elim_letrec) *) 19 | let e2' = f e2 in 20 | if S.mem x (fv e2') 21 | then LetRec ({ name = x, t; args = yts; body = f e1; annot }, e2') 22 | else (Format.eprintf "eliminating function %s@." x; e2') 23 | | LetTuple (xts, y, e) -> 24 | let xs = List.map fst xts in 25 | let e' = f e in 26 | let live = fv e' in 27 | if List.exists (fun x -> S.mem x live) xs 28 | then LetTuple (xts, y, e') 29 | else ( Format.eprintf "eliminating variables %s@." (Id.pp_list xs); e') 30 | | e -> e 31 | -------------------------------------------------------------------------------- /src/joe/elim.mli: -------------------------------------------------------------------------------- 1 | val f : KNormal.t -> KNormal.t 2 | -------------------------------------------------------------------------------- /src/joe/id.ml: -------------------------------------------------------------------------------- 1 | type t = string (* 変数の名前 (caml2html: id_t) *) [@@deriving show] 2 | type l = L of string (* トップレベル関数やグローバル配列のラベル (caml2html: id_l) *) [@@deriving show] 3 | 4 | let rec pp_list = function 5 | | [] -> "" 6 | | [x] -> x 7 | | x :: xs -> x ^ " " ^ pp_list xs 8 | 9 | let counter = ref 0 10 | let genid s = incr counter; Printf.sprintf "%s.%d" s !counter 11 | 12 | let rec id_of_typ = function 13 | | Type.Unit -> "u" 14 | | Type.Bool -> "b" 15 | | Type.Int -> "i" 16 | | Type.Float -> "d" 17 | | Type.Fun _ -> "f" 18 | | Type.Tuple _ -> "t" 19 | | Type.Array _ -> "a" 20 | | Type.Var _ -> assert false 21 | 22 | let gentmp typ = incr counter; Printf.sprintf "T%s%d" (id_of_typ typ) !counter 23 | 24 | -------------------------------------------------------------------------------- /src/joe/inline.ml: -------------------------------------------------------------------------------- 1 | open KNormal 2 | 3 | (* インライン展開する関数の最大サイズ (caml2html: inline_threshold) *) 4 | let threshold = ref 0 (* Mainで-inlineオプションによりセットされる *) 5 | 6 | let rec size = function 7 | | IfEq(_, _, e1, e2) | IfLE(_, _, e1, e2) 8 | | Let(_, e1, e2) | LetRec({ body = e1 }, e2) -> 1 + size e1 + size e2 9 | | LetTuple(_, _, e) -> 1 + size e 10 | | _ -> 1 11 | 12 | let rec g env = function (* インライン展開ルーチン本体 (caml2html: inline_g) *) 13 | | IfEq(x, y, e1, e2) -> IfEq(x, y, g env e1, g env e2) 14 | | IfLE(x, y, e1, e2) -> IfLE(x, y, g env e1, g env e2) 15 | | Let(xt, e1, e2) -> Let(xt, g env e1, g env e2) 16 | | LetRec({ name = (x, t); args = yts; body = e1 ; annot }, e2) -> (* 関数定義の場合 (caml2html: inline_letrec) *) 17 | let env = if size e1 > !threshold then env else M.add x (yts, e1) env in 18 | LetRec({ name = (x, t); args = yts; body = g env e1 ; annot }, g env e2) 19 | | App(x, ys) when M.mem x env -> (* 関数適用の場合 (caml2html: inline_app) *) 20 | let (zs, e) = M.find x env in Format.eprintf "inlining %s@." x; 21 | let env' = List.fold_left2 (fun env' (z, t) y -> M.add z y env') M.empty zs ys in Alpha.g env' e 22 | | LetTuple(xts, y, e) -> LetTuple(xts, y, g env e) 23 | | e -> e 24 | 25 | let f e = g M.empty e 26 | 27 | -------------------------------------------------------------------------------- /src/joe/inline.mli: -------------------------------------------------------------------------------- 1 | val threshold : int ref 2 | val f : KNormal.t -> KNormal.t 3 | -------------------------------------------------------------------------------- /src/joe/kNormal.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/joe/kNormal.ml -------------------------------------------------------------------------------- /src/joe/kNormal.mli: -------------------------------------------------------------------------------- 1 | type t = 2 | | Unit 3 | | Int of int 4 | | Float of float 5 | | Neg of Id.t 6 | | Add of Id.t * Id.t 7 | | Sub of Id.t * Id.t 8 | | Mul of Id.t * Id.t 9 | | Div of Id.t * Id.t 10 | | Mod of Id.t * Id.t 11 | | FNeg of Id.t 12 | | FAdd of Id.t * Id.t 13 | | FSub of Id.t * Id.t 14 | | FMul of Id.t * Id.t 15 | | FDiv of Id.t * Id.t 16 | | IfEq of Id.t * Id.t * t * t 17 | | IfLE of Id.t * Id.t * t * t 18 | | Let of (Id.t * Type.t) * t * t 19 | | Var of Id.t 20 | | LetRec of fundef * t 21 | | App of Id.t * Id.t list 22 | | Tuple of Id.t list 23 | | LetTuple of (Id.t * Type.t) list * Id.t * t 24 | | Get of Id.t * Id.t 25 | | Put of Id.t * Id.t * Id.t 26 | | ExtArray of Id.t 27 | | ExtFunApp of Id.t * Id.t list 28 | 29 | and fundef = 30 | { name : Id.t * Type.t 31 | ; args : (Id.t * Type.t) list 32 | ; body : t 33 | ; mutable annot : [ `TJ | `MJ ] option 34 | } 35 | 36 | val show : t -> string 37 | val show_fundef : fundef -> string 38 | val fv : t -> S.t 39 | val f : Syntax.t -> t 40 | -------------------------------------------------------------------------------- /src/joe/lexer.mll: -------------------------------------------------------------------------------- 1 | { (* lexerが利用する変数、関数、型などの定義 *) 2 | open Parser 3 | open Type 4 | } 5 | 6 | (* 正規表現の略記 *) 7 | let space = [' ' '\t' '\n' '\r'] 8 | let digit = ['0'-'9'] 9 | let lower = ['a'-'z'] 10 | let upper = ['A'-'Z'] 11 | 12 | rule token = parse 13 | | space+ { token lexbuf } 14 | | "(*" { comment lexbuf; (* ネストしたコメントのためのトリック *) token lexbuf } 15 | | '(' { LPAREN } 16 | | ')' { RPAREN } 17 | | "true" { BOOL(true) } 18 | | "false" { BOOL(false) } 19 | | "not" { NOT } 20 | | digit+ (* 整数を字句解析するルール (caml2html: lexer_int) *) { INT(int_of_string (Lexing.lexeme lexbuf)) } 21 | | digit+ ('.' digit*)? (['e' 'E'] ['+' '-']? digit+)? { FLOAT(float_of_string (Lexing.lexeme lexbuf)) } 22 | | '-' (* -.より後回しにしなくても良い? 最長一致? *) { MINUS } 23 | | '+' (* +.より後回しにしなくても良い? 最長一致? *) { PLUS } 24 | | '*' { AST } 25 | | '/' { SLASH } 26 | | "-." { MINUS_DOT } 27 | | "+." { PLUS_DOT } 28 | | "*." { AST_DOT } 29 | | "/." { SLASH_DOT } 30 | | '=' { EQUAL } 31 | | '%' { PERCENT } 32 | | "<>" { LESS_GREATER } 33 | | "<=" { LESS_EQUAL } 34 | | ">=" { GREATER_EQUAL } 35 | | '<' { LESS } 36 | | '>' { GREATER } 37 | | "if" { IF } 38 | | "then" { THEN } 39 | | "else" { ELSE } 40 | | "let" { LET } 41 | | "mod" { MOD } 42 | | "in" { IN } 43 | | "rec" { REC } 44 | | ',' { COMMA } 45 | | '_' { IDENT(Id.gentmp Type.Unit) } 46 | | "Array.create" | "Array.make" (* [XX] ad hoc *) { ARRAY_CREATE } 47 | | '.' { DOT } 48 | | "<-" { LESS_MINUS } 49 | | ';' { SEMICOLON } 50 | | ";;" { SEMISEMI } 51 | | '[' { LBRAC } 52 | | ']' { RBRAC } 53 | | '|' { VBAR } 54 | | eof { EOF } 55 | | lower (digit|lower|upper|'_')* (* 他の「予約語」より後でないといけない *) { IDENT(Lexing.lexeme lexbuf) } 56 | | _ { failwith (Printf.sprintf "unknown token %s near characters %d-%d" 57 | (Lexing.lexeme lexbuf) (Lexing.lexeme_start lexbuf) (Lexing.lexeme_end lexbuf)) } 58 | 59 | and comment = parse 60 | | "*)" { () } 61 | | "(*" { comment lexbuf; comment lexbuf } 62 | | eof { Format.eprintf "warning: unterminated comment@." } 63 | | _ { comment lexbuf } 64 | 65 | -------------------------------------------------------------------------------- /src/joe/m.ml: -------------------------------------------------------------------------------- 1 | (* customized version of Map *) 2 | 3 | module M = Map.Make (struct 4 | type t = Id.t 5 | 6 | let compare = compare 7 | end) 8 | 9 | include M 10 | 11 | let add_list xys env = List.fold_left (fun env (x, y) -> add x y env) env xys 12 | let add_list2 xs ys env = List.fold_left2 (fun env x y -> add x y env) env xs ys 13 | -------------------------------------------------------------------------------- /src/joe/main.ml: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | open BacCaml 3 | open Stdlib 4 | 5 | type backend = 6 | | Intel 7 | | ARM 8 | | Virtual 9 | 10 | let backend_type = ref ARM 11 | let debug = ref false 12 | let ast_dump = ref false 13 | let with_flag flag ~tru:f ~fls:g = if !flag then f () else g () 14 | 15 | let ast oc l = 16 | Id.counter := 0; 17 | Parser.exp Lexer.token l |> Syntax.show |> print_endline 18 | ;; 19 | 20 | let lexbuf oc l = 21 | Id.counter := 0; 22 | Typing.extenv := M.empty; 23 | Parser.exp Lexer.token l 24 | |> Typing.f 25 | |> KNormal.f 26 | |> Alpha.f 27 | |> Util.(iter !limit) 28 | |> Closure.f 29 | |> Virtual.f 30 | |> Simm.f 31 | |> fun p -> 32 | match !backend_type with 33 | | Intel -> X64.RegAlloc.f p |> X64.Emit.f oc 34 | | ARM -> Arm64.RegAlloc.f p |> Arm64.Emit.f oc 35 | | Virtual -> Stdlib.output_bytes oc (Marshal.to_bytes (Emit.f p) [Marshal.No_sharing]) 36 | 37 | let string s = lexbuf stdout (Lexing.from_string s) 38 | 39 | let open_rewrite f = open_out_gen [Open_binary;Open_wronly;Open_creat] 0o644 f 40 | 41 | 42 | let backend_type_to_arch = function 43 | | Intel -> "x64" 44 | | ARM -> "arm64" 45 | | Virtual -> failwith "Virtual machine does not have an architecture" 46 | 47 | let backend_type_to_suffix = function 48 | | Intel -> ".intel.s" 49 | | ARM -> ".arm.s" 50 | | Virtual -> ".joe" 51 | 52 | 53 | let write_file f f_without_filename sufix = 54 | let inchan = open_in f in 55 | let outchan = open_rewrite (f_without_filename ^ sufix) in 56 | try 57 | let input = Lexing.from_channel inchan in 58 | with_flag ast_dump 59 | ~tru:(fun _ -> ast outchan input) 60 | ~fls:(fun _ -> lexbuf outchan input; close_in inchan; close_out outchan); 61 | with 62 | | e -> 63 | close_in inchan; 64 | close_out outchan; 65 | raise e 66 | 67 | let main f = 68 | let f_without_filename = Filename.remove_extension f in 69 | let sufix = backend_type_to_suffix !backend_type in 70 | write_file f f_without_filename sufix; 71 | match !backend_type with 72 | | Intel | ARM -> 73 | ( let arch = backend_type_to_arch !backend_type in 74 | String.concat " " [ "gcc"; f_without_filename^sufix; 75 | "src/"^arch^"/libmincaml.c src/"^arch^"/stub.c -o "; 76 | f_without_filename ^ ".exe"] |> Sys.command |> ignore ) 77 | | _ -> () 78 | 79 | let () = 80 | let files = ref [] in 81 | Arg.parse 82 | [ ( "-inline", Arg.Int (fun i -> Inline.threshold := i) , "maximum size of functions inlined" ) ; 83 | ( "-iter", Arg.Int (fun i -> Util.limit := i), "maximum number of optimizations iterated" ) ; 84 | ( "-ast", Arg.Unit (fun _ -> ast_dump := true), "emit abstract syntax tree" ) ; 85 | ( "-x86", Arg.Unit (fun _ -> backend_type := Intel) , "emit IA32 machine code" ) ; 86 | ( "-x64", Arg.Unit (fun _ -> backend_type := Intel) , "emit EM64T machine code" ) ; 87 | ( "-arm" , Arg.Unit (fun _ -> backend_type := ARM) , "emit AArch64 machine code" ) ; 88 | ( "-vm" , Arg.Unit (fun _ -> backend_type := Virtual) , "emit MinCaml IR virtual machine" ) ; 89 | ( "-debug", Arg.Unit (fun _ -> debug := true), "enable debug mode" ) 90 | ] 91 | (fun s -> files := !files @ [ s ]) 92 | ("MinCaml EM64T/AArch64/IR Compiler (c) 2024 Namdak Tonpa\n" 93 | ^ "usage: joe [-inline m] [-iter n] ... filenames" ); 94 | List.iter (fun f -> main f) !files 95 | 96 | -------------------------------------------------------------------------------- /src/joe/main.mli: -------------------------------------------------------------------------------- 1 | val main : string -> unit 2 | -------------------------------------------------------------------------------- /src/joe/parser.mly: -------------------------------------------------------------------------------- 1 | %{ 2 | (* parserが利用する変数、関数、型などの定義 *) 3 | open Syntax 4 | let addtyp x = (x, Type.gentyp ()) 5 | let annot_of_var var = if var = "mj" then Some `MJ else if var = "tj" then Some `TJ else None 6 | %} 7 | 8 | /* (* 字句を表すデータ型の定義 (caml2html: parser_token) *) */ 9 | %token BOOL 10 | %token INT 11 | %token FLOAT 12 | %token NOT 13 | %token MINUS 14 | %token PLUS 15 | %token AST 16 | %token SLASH 17 | %token MINUS_DOT 18 | %token PLUS_DOT 19 | %token AST_DOT 20 | %token SLASH_DOT 21 | %token EQUAL 22 | %token LESS_GREATER 23 | %token LESS_EQUAL 24 | %token GREATER_EQUAL 25 | %token LESS 26 | %token GREATER 27 | %token IF 28 | %token THEN 29 | %token ELSE 30 | %token MOD 31 | %token IDENT 32 | %token LET 33 | %token IN 34 | %token REC 35 | %token COMMA 36 | %token ARRAY_CREATE 37 | %token DOT 38 | %token LESS_MINUS 39 | %token SEMICOLON 40 | %token SEMISEMI 41 | %token LPAREN 42 | %token RPAREN 43 | %token LBRAC 44 | %token RBRAC 45 | %token VBAR 46 | %token PERCENT 47 | %token EOF 48 | 49 | /* (* 優先順位とassociativityの定義(低い方から高い方へ) (caml2html: parser_prior) *) */ 50 | %nonassoc IN 51 | %right prec_let 52 | %right SEMICOLON 53 | %right prec_if 54 | %right LESS_MINUS 55 | %nonassoc prec_tuple 56 | %left COMMA 57 | %left EQUAL LESS_GREATER LESS GREATER LESS_EQUAL GREATER_EQUAL 58 | %left PLUS MINUS PLUS_DOT MINUS_DOT MOD 59 | %left AST_DOT SLASH_DOT 60 | %right prec_unary_minus 61 | %left prec_app 62 | %left DOT 63 | 64 | /* (* 開始記号の定義 *) */ 65 | %type exp 66 | %start exp 67 | 68 | %% 69 | 70 | simple_exp: /* (* 括弧をつけなくても関数の引数になれる式 (caml2html: parser_simple) *) */ 71 | | LPAREN exp RPAREN { $2 } 72 | | LPAREN RPAREN { Unit } 73 | | BOOL { Bool($1) } 74 | | INT { Int($1) } 75 | | FLOAT { Float($1) } 76 | | IDENT { Var($1) } 77 | | simple_exp DOT LPAREN exp RPAREN { Get($1, $4) } 78 | 79 | exp: /* (* 一般の式 (caml2html: parser_exp) *) */ 80 | | simple_exp { $1 } 81 | | NOT exp %prec prec_app { Not($2) } /* (* -1.23などは型エラーではないので別扱い *) */ 82 | | MINUS exp %prec prec_unary_minus { match $2 with | Float(f) -> Float(-.f) | e -> Neg(e) } 83 | | exp PLUS exp { Add($1, $3) } 84 | | exp MINUS exp { Sub($1, $3) } 85 | | exp EQUAL exp { Eq($1, $3) } 86 | | exp LESS_GREATER exp { Not(Eq($1, $3)) } 87 | | exp AST exp { Mul($1, $3)} 88 | | exp MOD exp { Mod ($1, $3) } 89 | | exp SLASH exp { Div($1, $3) } 90 | | exp LESS exp { Not(LE($3, $1)) } 91 | | exp GREATER exp { Not(LE($1, $3)) } 92 | | exp LESS_EQUAL exp { LE($1, $3) } 93 | | exp GREATER_EQUAL exp { LE($3, $1) } 94 | | IF exp THEN exp ELSE exp %prec prec_if { If($2, $4, $6) } 95 | | MINUS_DOT exp %prec prec_unary_minus { FNeg($2) } 96 | | exp PLUS_DOT exp { FAdd($1, $3) } 97 | | exp MINUS_DOT exp { FSub($1, $3) } 98 | | exp AST_DOT exp { FMul($1, $3) } 99 | | exp SLASH_DOT exp { FDiv($1, $3) } 100 | | LET IDENT EQUAL exp IN exp %prec prec_let { Let(addtyp $2, $4, $6) } 101 | | LET REC fundef SEMISEMI exp %prec prec_let { LetRec($3, $5) } 102 | | LET REC fundef IN exp %prec prec_let { LetRec($3, $5) } 103 | | LET PERCENT IDENT REC fundef IN exp %prec prec_let { let fundef = $5 in fundef.annot <- annot_of_var $3; LetRec(fundef, $7) } 104 | | simple_exp actual_args %prec prec_app { App($1, $2) } 105 | | elems %prec prec_tuple { Tuple($1) } 106 | | LET LPAREN pat RPAREN EQUAL exp IN exp { LetTuple($3, $6, $8) } 107 | | simple_exp DOT LPAREN exp RPAREN LESS_MINUS exp { Put($1, $4, $7) } 108 | | exp SEMICOLON exp { Let((Id.gentmp Type.Unit, Type.Unit), $1, $3) } 109 | | ARRAY_CREATE simple_exp simple_exp %prec prec_app { Array($2, $3) } 110 | | LET IDENT EQUAL lst IN exp 111 | { 112 | match $4 with 113 | | List (x) -> let create_array lst = let rec loop i = function 114 | | [] -> $6 115 | | hd :: tl -> Let((Id.gentmp Type.Unit, Type.Unit), Put (Var $2, Int i, hd), loop (i + 1) tl) 116 | in loop 0 lst in Let (addtyp $2, Array (Int (List.length x), Int (0)), create_array x) 117 | | _ -> failwith "list should be come here." 118 | } 119 | | error { failwith (Printf.sprintf "parse error near characters %d-%d" 120 | (Parsing.symbol_start ()) (Parsing.symbol_end ())) } 121 | 122 | fundef: 123 | | IDENT formal_args EQUAL exp { { name = addtyp $1; args = $2; body = $4; annot = None } } 124 | 125 | formal_args: 126 | | IDENT formal_args { addtyp $1 :: $2 } 127 | | IDENT { [addtyp $1] } 128 | 129 | actual_args: 130 | | actual_args simple_exp %prec prec_app { $1 @ [$2] } 131 | | simple_exp %prec prec_app { [$1] } 132 | 133 | elems: 134 | | elems COMMA exp { $1 @ [$3] } 135 | | exp COMMA exp { [$1; $3] } 136 | 137 | pat: 138 | | pat COMMA IDENT { $1 @ [addtyp $3] } 139 | | IDENT COMMA IDENT { [addtyp $1; addtyp $3] } 140 | 141 | lstcont: 142 | | { [] } 143 | | simple_exp { [$1] } 144 | | simple_exp SEMICOLON lstcont { $1 :: $3 } 145 | 146 | lst: 147 | | LBRAC VBAR lstcont VBAR RBRAC { List $3 } 148 | -------------------------------------------------------------------------------- /src/joe/s.ml: -------------------------------------------------------------------------------- 1 | (* customized version of Set *) 2 | 3 | module S = Set.Make (struct 4 | type t = Id.t 5 | let compare = compare 6 | end) 7 | 8 | include S 9 | 10 | let of_list l = List.fold_left (fun s e -> add e s) empty l 11 | -------------------------------------------------------------------------------- /src/joe/simm.ml: -------------------------------------------------------------------------------- 1 | open Asm 2 | 3 | let rec g env = function (* 命令列の16bit即値最適化 (caml2html: simm13_g) *) 4 | | Ans(exp) -> Ans(g' env exp) 5 | | Let((x, t), Li(i), e) when -32768 <= i && i < 32768 -> 6 | (* Format.eprintf "found simm16 %s = %d@." x i; *) 7 | let e' = g (M.add x i env) e in if List.mem x (fv e') then Let((x, t), Li(i), e') else 8 | ((* Format.eprintf "erased redundant Set to %s@." x; *) e') 9 | | Let(xt, Slw(y, C(i)), e) when M.mem y env -> (* for array access *) 10 | (* Format.eprintf "erased redundant Slw on %s@." x; *) g env (Let(xt, Li((M.find y env) lsl i), e)) 11 | | Let(xt, exp, e) -> Let(xt, g' env exp, g env e) 12 | 13 | and g' env = function (* 各命令の16bit即値最適化 (caml2html: simm13_gprime) *) 14 | | Add (x, V y) when M.mem y env -> Add (x, C (M.find y env)) 15 | | Add (x, V y) when M.mem x env -> Add (y, C (M.find x env)) 16 | | Sub (x, V y) when M.mem y env -> Sub (x, C (M.find y env)) 17 | | Sub (x, V y) when M.mem x env -> Sub (y, C (M.find x env)) 18 | | Mul (x, V y) when M.mem y env -> Mul (x, C (M.find y env)) 19 | | Mul (x, V y) when M.mem x env -> Mul (y, C (M.find x env)) 20 | | Div (x, V y) when M.mem y env -> Div (x, C (M.find y env)) 21 | | Div (x, V y) when M.mem x env -> Div (y, C (M.find x env)) 22 | | Mod (x, V y) when M.mem y env -> Mod (x, C (M.find y env)) 23 | | Mod (x, V y) when M.mem x env -> Mod (y, C (M.find x env)) 24 | | Ld (x, V y, i) when M.mem y env -> Ld (x, C (M.find y env), i) 25 | | St (x, y, V z, i) when M.mem z env -> St (x, y, C (M.find z env), i) 26 | | LdDF (x, V y, i) when M.mem y env -> LdDF (x, C (M.find y env), i) 27 | | StDF (x, y, V z, i) when M.mem z env -> StDF (x, y, C (M.find z env), i) 28 | | IfEq (x, V y, e1, e2) when M.mem y env -> IfEq (x, C (M.find y env), g env e1, g env e2) 29 | | IfLE (x, V y, e1, e2) when M.mem y env -> IfLE (x, C (M.find y env), g env e1, g env e2) 30 | | IfGE (x, V y, e1, e2) when M.mem y env -> IfGE (x, C (M.find y env), g env e1, g env e2) 31 | | IfEq (x, V y, e1, e2) when M.mem x env -> IfEq (y, C (M.find x env), g env e1, g env e2) 32 | | IfLE (x, V y, e1, e2) when M.mem x env -> IfGE (y, C (M.find x env), g env e1, g env e2) 33 | | IfGE (x, V y, e1, e2) when M.mem x env -> IfLE (y, C (M.find x env), g env e1, g env e2) 34 | | IfEq (x, y', e1, e2) -> IfEq (x, y', g env e1, g env e2) 35 | | IfLE (x, y', e1, e2) -> IfLE (x, y', g env e1, g env e2) 36 | | IfGE (x, y', e1, e2) -> IfGE (x, y', g env e1, g env e2) 37 | | IfFEq (x, y, e1, e2) -> IfFEq (x, y, g env e1, g env e2) 38 | | IfFLE (x, y, e1, e2) -> IfFLE (x, y, g env e1, g env e2) 39 | | e -> e 40 | 41 | let h { name = l; args = xs; fargs = ys; body = e; ret = t; annot } = (* トップレベル関数の16bit即値最適化 *) 42 | { name = l; args = xs; fargs = ys; body = g M.empty e; ret = t; annot } 43 | 44 | let f (Prog (data, fundefs, e)) = (* プログラム全体の16bit即値最適化 *) 45 | Prog (data, List.map h fundefs, g M.empty e) 46 | -------------------------------------------------------------------------------- /src/joe/simm.mli: -------------------------------------------------------------------------------- 1 | val f : Asm.prog -> Asm.prog 2 | -------------------------------------------------------------------------------- /src/joe/syntax.ml: -------------------------------------------------------------------------------- 1 | type t = (* MinCamlの構文を表現するデータ型 (caml2html: syntax_t) *) 2 | | Unit 3 | | Bool of bool 4 | | Int of int 5 | | Float of float 6 | | Not of t 7 | | Neg of t 8 | | Add of t * t 9 | | Mul of t * t 10 | | Div of t * t 11 | | Sub of t * t 12 | | Mod of t * t 13 | | FNeg of t 14 | | FAdd of t * t 15 | | FSub of t * t 16 | | FMul of t * t 17 | | FDiv of t * t 18 | | Eq of t * t 19 | | LE of t * t 20 | | If of t * t * t 21 | | Let of (Id.t * Type.t) * t * t 22 | | Var of Id.t 23 | | LetRec of fundef * t 24 | | App of t * t list 25 | | Tuple of t list 26 | | LetTuple of (Id.t * Type.t) list * t * t 27 | | Array of t * t 28 | | Get of t * t 29 | | Put of t * t * t 30 | | List of t list [@@deriving show] 31 | 32 | and fundef = 33 | { name : Id.t * Type.t 34 | ; args : (Id.t * Type.t) list 35 | ; body : t 36 | ; mutable annot : [ `TJ | `MJ ] option 37 | } [@@deriving show] 38 | 39 | let rec print_t = function 40 | | Unit -> print_string "Unit" 41 | | Bool v -> Printf.printf "Bool(%s)" (string_of_bool v) 42 | | Int v -> Printf.printf "Int(%s)" (string_of_int v) 43 | | Float v -> Printf.printf "Float(%s)" (string_of_float v) 44 | | Not t -> print_string "Not("; print_t t; print_string ")" 45 | | Neg t -> print_string "Neg("; print_t t; print_string ")" 46 | | Add (t1, t2) -> print_string "Add("; print_t t1; print_string ", "; print_t t2; print_string ")"; print_string ")" 47 | | Sub (t1, t2) -> print_string "Sub("; print_t t1; print_string ", "; print_t t2; print_string ")" 48 | | FNeg t -> print_string "FNeg("; print_t t; print_string ")" 49 | | FAdd (t1, t2) -> print_string "FAdd("; print_t t1; print_string ", "; print_t t2; print_string ")" 50 | | FSub (t1, t2) -> print_string "FSub("; print_t t1; print_t t2; print_string ")" 51 | | FMul (t1, t2) -> print_string "FMul("; print_t t1; print_string ", "; print_t t2; print_string ")" 52 | | FDiv (t1, t2) -> print_string "FDiv("; print_t t1; print_string ", "; print_t t2; print_string ")" 53 | | Eq (t1, t2) -> print_string "Eq("; print_t t1; print_string ", "; print_t t2; print_string ")" 54 | | LE (t1, t2) -> 55 | print_string "LE("; 56 | print_t t1; 57 | print_string ", "; 58 | print_t t2; 59 | print_string ")" 60 | | If (t1, t2, t3) -> 61 | print_string "If("; 62 | print_t t1; 63 | print_string ", "; 64 | print_newline (); 65 | print_t t2; 66 | print_string ", "; 67 | print_newline (); 68 | print_t t3; 69 | print_string ")" 70 | | Let ((id, typ), t1, t2) -> 71 | print_string "Let("; 72 | print_string "("; 73 | print_string id; 74 | print_string ","; 75 | Type.print_t typ; 76 | print_string "), "; 77 | print_t t1; 78 | print_string ", "; 79 | print_newline (); 80 | print_t t2; 81 | print_string ")" 82 | | Var id -> 83 | print_string "Var("; 84 | print_string id; 85 | print_string ")" 86 | | LetRec (fundef, t) -> 87 | print_string "LetRec("; 88 | print_fundef fundef; 89 | print_string ", "; 90 | print_t t; 91 | print_string ")" 92 | | App (t, ts) -> 93 | print_string "App("; 94 | print_t t; 95 | print_string ", "; 96 | print_t_list ts; 97 | print_string ")" 98 | | Tuple ts -> 99 | print_string "Tuple("; 100 | print_t_list ts; 101 | print_string ")" 102 | | LetTuple (args, t1, t2) -> 103 | let rec print_args args = 104 | match args with 105 | | [] -> () 106 | | [ (id, typ) ] -> 107 | print_string id; 108 | print_string ", "; 109 | Type.print_t typ 110 | | (id, typ) :: tl -> 111 | print_string id; 112 | print_string ", "; 113 | Type.print_t typ; 114 | print_args tl 115 | in 116 | print_string "LetTuple("; 117 | print_string "("; 118 | print_args args; 119 | print_string ")"; 120 | print_t t1; 121 | print_string ", "; 122 | print_t t2; 123 | print_string ")" 124 | | Array (t1, t2) -> 125 | print_string "Array("; 126 | print_t t1; 127 | print_string ", "; 128 | print_t t2; 129 | print_string ")" 130 | | Get (t1, t2) -> 131 | print_string "Get("; 132 | print_t t1; 133 | print_string ", "; 134 | print_t t2; 135 | print_string ")" 136 | | Put (t1, t2, t3) -> 137 | print_t t1; 138 | print_string ", "; 139 | print_t t2; 140 | print_string ", "; 141 | print_t t3; 142 | print_string ")" 143 | | _ -> assert false 144 | 145 | and print_fundef { name; args; body } = 146 | let id, typ = name in 147 | print_string "{"; 148 | print_string "("; 149 | print_string id; 150 | Type.print_t typ; 151 | print_string "); "; 152 | print_string "["; 153 | List.iter 154 | (fun arg -> 155 | let id, typ = arg in 156 | print_string id; 157 | print_string ", "; 158 | Type.print_t typ) 159 | args; 160 | print_t body; 161 | print_string "}" 162 | 163 | and print_t_list ts = 164 | print_string "["; 165 | let rec loop ts = 166 | match ts with 167 | | [] -> () 168 | | [ hd ] -> print_t hd 169 | | hd :: tl -> 170 | print_t hd; 171 | print_string "; "; 172 | loop tl 173 | in 174 | loop ts; 175 | print_string "]" 176 | ;; 177 | -------------------------------------------------------------------------------- /src/joe/type.ml: -------------------------------------------------------------------------------- 1 | type t = (* MinCamlの型を表現するデータ型 (caml2html: type_t) *) 2 | | Unit 3 | | Bool 4 | | Int 5 | | Float 6 | | Fun of t list * t (* arguments are uncurried *) 7 | | Tuple of t list 8 | | Array of t 9 | | Var of t option ref [@@deriving show] 10 | 11 | let gentyp () = Var (ref None) (* 新しい型変数を作る *) 12 | 13 | let rec print_t = function 14 | | Unit -> print_string "Unit" 15 | | Bool -> print_string "Bool" 16 | | Int -> print_string "Int" 17 | | Float -> print_string "Float" 18 | | Fun (ts, t) -> print_string "Fun("; List.iter (fun t -> print_t t; print_string ", ") ts; print_t t; print_string ")" 19 | | Tuple ts -> let count = ref 0 in print_string "Tuple("; List.iter (fun t -> if !count = List.length ts then print_t t else print_t t; print_string ", "; incr count) ts; print_string ")" 20 | | Array t -> print_string "Array("; print_t t; print_string ")" 21 | | Var opt_t_ref -> print_string "Var("; (match !opt_t_ref with Some v -> print_t v | None -> print_string "None"); print_string ")" 22 | 23 | -------------------------------------------------------------------------------- /src/joe/typing.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/joe/typing.ml -------------------------------------------------------------------------------- /src/joe/typing.mli: -------------------------------------------------------------------------------- 1 | exception Error of Syntax.t * Type.t * Type.t 2 | 3 | val extenv : Type.t M.t ref 4 | val f : Syntax.t -> Syntax.t 5 | -------------------------------------------------------------------------------- /src/joe/unparser.ml: -------------------------------------------------------------------------------- 1 | open Syntax 2 | 3 | let rec string_of_ast (t : Syntax.t) : string = match t with 4 | | Unit -> "()" 5 | | Int n -> string_of_int n 6 | | Bool b -> string_of_bool b 7 | | Float f -> string_of_float f 8 | | Not t -> Printf.sprintf "(not (%s))" (string_of_ast t) 9 | | Neg t -> Printf.sprintf "(- %s)" (string_of_ast t) 10 | | FNeg t -> Printf.sprintf "(-. %s)" (string_of_ast t) 11 | | Var x -> x 12 | | Add (t1, t2) -> Printf.sprintf "%s + %s" (string_of_ast t1) (string_of_ast t2) 13 | | Sub (t1, t2) -> Printf.sprintf "%s - %s" (string_of_ast t1) (string_of_ast t2) 14 | | FAdd (t1, t2) -> Printf.sprintf "%s +. %s" (string_of_ast t1) (string_of_ast t2) 15 | | FSub (t1, t2) -> Printf.sprintf "%s -. %s" (string_of_ast t1) (string_of_ast t2) 16 | | FMul (t1, t2) -> Printf.sprintf "%s *. %s" (string_of_ast t1) (string_of_ast t2) 17 | | FDiv (t1, t2) -> Printf.sprintf "%s /. %s" (string_of_ast t1) (string_of_ast t2) 18 | | Eq (t1, t2) -> Printf.sprintf "%s = %s" (string_of_ast t1) (string_of_ast t2) 19 | | LE (t1, t2) -> Printf.sprintf "%s <= %s" (string_of_ast t1) (string_of_ast t2) 20 | | If (t1, t2, t3) -> Printf.sprintf "if %s then %s\nelse %s" (string_of_ast t1) (string_of_ast t2) (string_of_ast t3) 21 | | Let ((id, typ), t1, t2) -> 22 | if String.contains id 'T' then Printf.sprintf "let _ = %s in %s" (string_of_ast t1) (string_of_ast t2) 23 | else Printf.sprintf "let %s = %s in %s" id (string_of_ast t1) (string_of_ast t2) 24 | | LetRec (fundef, t) -> let { name; args; body } = fundef in 25 | let id, _ = name in let args' = List.fold_left (fun acc (id, _) -> 26 | if String.contains id 'T' && String.contains id 'u' 27 | then acc ^ "_" ^ " " else acc ^ id ^ " ") "" args 28 | in Printf.sprintf "let rec %s %s= %s\nin %s" id args' (string_of_ast body) (string_of_ast t) 29 | | App (t, ts) -> let rec loop = function 30 | | [] -> "" 31 | | [ hd ] -> string_of_ast hd 32 | | hd :: tl -> string_of_ast hd ^ " " ^ loop tl 33 | in Printf.sprintf "%s (%s)" (string_of_ast t) (loop ts) 34 | | Tuple ts -> Printf.sprintf "(%s)" (string_of_ast_ts ts) 35 | | LetTuple (xs, t1, t2) -> let rec loop = function 36 | | [] -> "" 37 | | [ (id, _) ] -> id 38 | | (id, _) :: tl -> id ^ " " ^ loop tl 39 | in Printf.sprintf "let %s = %s in %s" (loop xs) (string_of_ast t1) (string_of_ast t2) 40 | | Array (t1, t2) -> Printf.sprintf "Array.create %s %s" (string_of_ast t1) (string_of_ast t2) 41 | | Get (t1, t2) -> Printf.sprintf "%s.(%s)" (string_of_ast t1) (string_of_ast t2) 42 | | Put (t1, t2, t3) -> Printf.sprintf "%s.(%s) <- %s" (string_of_ast t1) (string_of_ast t2) (string_of_ast t3) 43 | 44 | and string_of_ast_ts = function 45 | | [] -> "" 46 | | [ hd ] -> string_of_ast hd 47 | | hd :: tl -> string_of_ast hd ^ ", " ^ string_of_ast_ts tl 48 | 49 | let unparse (t : Syntax.t) = string_of_ast t |> print_endline 50 | let parse_from_string s = Lexing.from_string s |> Parser.exp Lexer.token 51 | -------------------------------------------------------------------------------- /src/joe/unparser.mli: -------------------------------------------------------------------------------- 1 | val string_of_ast : Syntax.t -> string 2 | val unparse : Syntax.t -> unit 3 | val parse_from_string : string -> Syntax.t 4 | -------------------------------------------------------------------------------- /src/joe/util.ml: -------------------------------------------------------------------------------- 1 | let limit = ref 1000 2 | 3 | let rec iter n e = 4 | Format.eprintf "iteration %d@." n; 5 | if n = 0 then e 6 | else (let e' = Elim.f (ConstFold.f (Inline.f (Assoc.f (Beta.f e)))) in if e = e' then e else iter (n - 1) e') 7 | 8 | -------------------------------------------------------------------------------- /src/joe/virtual.ml: -------------------------------------------------------------------------------- 1 | (* translation into assembly with infinite number of virtual registers *) 2 | 3 | open Asm 4 | open X64 5 | 6 | let data = ref [] (* 浮動小数点数の定数テーブル (caml2html: virtual_data) *) 7 | 8 | let classify xts ini addf addi = List.fold_left (fun acc (x, t) -> 9 | match t with 10 | | Type.Unit -> acc 11 | | Type.Float -> addf acc x 12 | | _ -> addi acc x t) ini xts 13 | 14 | let separate xts = 15 | classify xts ([], []) (fun (int, float) x -> int, float @ [ x ]) 16 | (fun (int, float) x _ -> int @ [ x ], float) 17 | 18 | let expand xts ini addf addi = 19 | classify xts ini (fun (offset, acc) x -> let offset = align offset in offset + 8, addf x offset acc) 20 | (fun (offset, acc) x t -> (* NOTE: 64ビットなので4バイトから8バイトにする *) offset + 8, addi x t offset acc) 21 | 22 | let rec g env = function (* 式の仮想マシンコード生成 (caml2html: virtual_g) *) 23 | | Closure.Unit -> Ans Nop 24 | | Closure.Int i -> Ans (Li i) 25 | | Closure.Float d -> let l = try 26 | (* すでに定数テーブルにあったら再利用 Cf. https://github.com/esumii/min-caml/issues/13 *) 27 | let l, _ = List.find (fun (_, d') -> d = d') !data in l 28 | with | Not_found -> let l = Id.L (Id.genid "l") in data := (l, d) :: !data; l 29 | in let x = Id.genid "l" in Let ((x, Type.Int), SetL l, Ans (LdDF (x, C 0, 1))) 30 | | Closure.Neg x -> Ans (Neg x) 31 | | Closure.Add (x, y) -> Ans (Add (x, V y)) 32 | | Closure.Sub (x, y) -> Ans (Sub (x, V y)) 33 | | Closure.Mul (x, y) -> Ans (Mul (x, V y)) 34 | | Closure.Div (x, y) -> Ans (Div (x, V y)) 35 | | Closure.Mod (x, y) -> Ans (Mod (x, V y)) 36 | | Closure.FNeg x -> Ans (FNeg x) 37 | | Closure.FAdd (x, y) -> Ans (FAdd (x, y)) 38 | | Closure.FSub (x, y) -> Ans (FSub (x, y)) 39 | | Closure.FMul (x, y) -> Ans (FMul (x, y)) 40 | | Closure.FDiv (x, y) -> Ans (FDiv (x, y)) 41 | | Closure.IfEq (x, y, e1, e2) -> 42 | (match M.find x env with 43 | | Type.Bool | Type.Int -> Ans (IfEq (x, V y, g env e1, g env e2)) 44 | | Type.Float -> Ans (IfFEq (x, y, g env e1, g env e2)) 45 | | _ -> failwith "equality supported only for bool, int, and float") 46 | | Closure.IfLE (x, y, e1, e2) -> 47 | (match M.find x env with 48 | | Type.Bool | Type.Int -> Ans (IfLE (x, V y, g env e1, g env e2)) 49 | | Type.Float -> Ans (IfFLE (x, y, g env e1, g env e2)) 50 | | _ -> failwith "inequality supported only for bool, int, and float") 51 | | Closure.Let ((x, t1), e1, e2) -> 52 | let e1' = g env e1 in 53 | let e2' = g (M.add x t1 env) e2 in 54 | concat e1' (x, t1) e2' 55 | | Closure.Var x -> 56 | (match M.find x env with 57 | | Type.Unit -> Ans Nop 58 | | Type.Float -> Ans (FMov x) 59 | | _ -> Ans (Mov x)) 60 | | Closure.MakeCls ((x, t), { Closure.entry = l; Closure.actual_fv = ys }, e2) -> (* クロージャの生成 (caml2html: virtual_makecls) *) 61 | (* Closureのアドレスをセットしてから、自由変数の値をストア *) 62 | let e2' = g (M.add x t env) e2 in 63 | let offset, store_fv = 64 | expand 65 | (List.map (fun y -> y, M.find y env) ys) 66 | (* NOTE: 64ビットなので4バイトから8バイトにする *) 67 | (8, e2') 68 | (fun y offset store_fv -> seq (StDF (y, x, C offset, 1), store_fv)) 69 | (fun y _ offset store_fv -> seq (St (y, x, C offset, 1), store_fv)) 70 | in 71 | Let 72 | ( (x, t) 73 | , Mov reg_hp 74 | , Let 75 | ( (reg_hp, Type.Int) 76 | , Add (reg_hp, C (align offset)) 77 | , let z = Id.genid "l" in 78 | Let ((z, Type.Int), SetL l, seq (St (z, x, C 0, 1), store_fv)) ) ) 79 | | Closure.AppCls (x, ys) -> 80 | let int, float = separate (List.map (fun y -> y, M.find y env) ys) in 81 | Ans (CallCls (x, int, float)) 82 | | Closure.AppDir (Id.L x, ys) -> 83 | let int, float = separate (List.map (fun y -> y, M.find y env) ys) in 84 | Ans (CallDir (Id.L x, int, float)) 85 | | Closure.Tuple xs -> (* 組の生成 (caml2html: virtual_tuple) *) 86 | let y = Id.genid "t" in 87 | let offset, store = 88 | expand 89 | (List.map (fun x -> x, M.find x env) xs) 90 | (0, Ans (Mov y)) 91 | (fun x offset store -> seq (StDF (x, y, C offset, 1), store)) 92 | (fun x _ offset store -> seq (St (x, y, C offset, 1), store)) 93 | in 94 | Let 95 | ( (y, Type.Tuple (List.map (fun x -> M.find x env) xs)) 96 | , Mov reg_hp 97 | , Let ((reg_hp, Type.Int), Add (reg_hp, C (align offset)), store) ) 98 | | Closure.LetTuple (xts, y, e2) -> 99 | let s = Closure.fv e2 in 100 | let offset, load = 101 | expand 102 | xts 103 | (0, g (M.add_list xts env) e2) 104 | (fun x offset load -> 105 | if not (S.mem x s) 106 | then load else (* [XX] a little ad hoc optimization *) 107 | fletd (x, LdDF (y, C offset, 1), load)) 108 | (fun x t offset load -> 109 | if not (S.mem x s) 110 | then load else (* [XX] a little ad hoc optimization *) 111 | Let ((x, t), Ld (y, C offset, 1), load)) 112 | in 113 | load 114 | | Closure.Get (x, y) -> (* 配列の読み出し (caml2html: virtual_get) *) 115 | (match M.find x env with 116 | | Type.Array Type.Unit -> Ans Nop 117 | | Type.Array Type.Float -> Ans (LdDF (x, V y, 8)) 118 | | Type.Array _ -> Ans (Ld (x, V y, 8)) 119 | | _ -> assert false) 120 | | Closure.Put (x, y, z) -> 121 | (match M.find x env with 122 | | Type.Array Type.Unit -> Ans Nop 123 | | Type.Array Type.Float -> Ans (StDF (z, x, V y, 8)) 124 | | Type.Array _ -> Ans (St (z, x, V y, 8)) 125 | | _ -> assert false) 126 | | Closure.ExtArray (Id.L x) -> Ans (SetL (Id.L ("min_caml_" ^ x))) 127 | 128 | (* 関数の仮想マシンコード生成 (caml2html: virtual_h) *) 129 | let h 130 | { Closure.name = Id.L x, t 131 | ; Closure.args = yts 132 | ; Closure.formal_fv = zts 133 | ; Closure.body = e 134 | ; annot 135 | } 136 | = let int, float = separate yts in 137 | let offset, load = expand zts 138 | (* NOTE: 64ビットなので4バイトから8バイトにする *) 139 | (8, g (M.add x t (M.add_list yts (M.add_list zts M.empty))) e) 140 | (fun z offset load -> fletd (z, LdDF (x, C offset, 1), load)) 141 | (fun z t offset load -> Let ((z, t), Ld (x, C offset, 1), load)) 142 | in match t with 143 | | Type.Fun (_, t2) -> { name = Id.L x; args = int; fargs = float; body = load; ret = t2; annot } 144 | | _ -> assert false 145 | 146 | (* プログラム全体の仮想マシンコード生成 (caml2html: virtual_f) *) 147 | let f (Closure.Prog (fundefs, e)) = 148 | data := []; 149 | let fundefs = List.map h fundefs in 150 | let e = g M.empty e in Prog (!data, fundefs, e) 151 | -------------------------------------------------------------------------------- /src/joe/virtual.mli: -------------------------------------------------------------------------------- 1 | val f : Closure.prog -> Asm.prog 2 | -------------------------------------------------------------------------------- /src/sparc/emit.ml: -------------------------------------------------------------------------------- 1 | open Asm 2 | 3 | external gethi : float -> int32 = "gethi" 4 | external getlo : float -> int32 = "getlo" 5 | 6 | let stackset = ref S.empty (* すでにSaveされた変数の集合 *) 7 | let stackmap = ref [] (* Saveされた変数の、スタックにおける位置 *) 8 | let save x = 9 | stackset := S.add x !stackset; 10 | if not (List.mem x !stackmap) then 11 | stackmap := !stackmap @ [x] 12 | let savef x = 13 | stackset := S.add x !stackset; 14 | if not (List.mem x !stackmap) then 15 | (let pad = 16 | if List.length !stackmap mod 2 = 0 then [] else [Id.gentmp Type.Int] in 17 | stackmap := !stackmap @ pad @ [x; x]) 18 | let locate x = 19 | let rec loc = function 20 | | [] -> [] 21 | | y :: zs when x = y -> 0 :: List.map succ (loc zs) 22 | | y :: zs -> List.map succ (loc zs) in 23 | loc !stackmap 24 | let offset x = 4 * List.hd (locate x) 25 | let stacksize () = align ((List.length !stackmap + 1) * 4) 26 | 27 | let pp_id_or_imm = function 28 | | V(x) -> x 29 | | C(i) -> string_of_int i 30 | 31 | (* 関数呼び出しのために引数を並べ替える(register shuffling) *) 32 | let rec shuffle sw xys = 33 | (* remove identical moves *) 34 | let _, xys = List.partition (fun (x, y) -> x = y) xys in 35 | (* find acyclic moves *) 36 | match List.partition (fun (_, y) -> List.mem_assoc y xys) xys with 37 | | [], [] -> [] 38 | | (x, y) :: xys, [] -> (* no acyclic moves; resolve a cyclic move *) 39 | (y, sw) :: (x, y) :: shuffle sw (List.map 40 | (function 41 | | (y', z) when y = y' -> (sw, z) 42 | | yz -> yz) 43 | xys) 44 | | xys, acyc -> acyc @ shuffle sw xys 45 | 46 | type dest = Tail | NonTail of Id.t (* 末尾かどうかを表すデータ型 *) 47 | let rec g oc = function (* 命令列のアセンブリ生成 *) 48 | | dest, Ans(exp) -> g' oc (dest, exp) 49 | | dest, Let((x, t), exp, e) -> 50 | g' oc (NonTail(x), exp); 51 | g oc (dest, e) 52 | and g' oc = function (* 各命令のアセンブリ生成 *) 53 | (* 末尾でなかったら計算結果をdestにセット *) 54 | | NonTail(_), Nop -> () 55 | | NonTail(x), Set(i) -> Printf.fprintf oc "\tset\t%d, %s\n" i x 56 | | NonTail(x), SetL(Id.L(y)) -> Printf.fprintf oc "\tset\t%s, %s\n" y x 57 | | NonTail(x), Mov(y) when x = y -> () 58 | | NonTail(x), Mov(y) -> Printf.fprintf oc "\tmov\t%s, %s\n" y x 59 | | NonTail(x), Neg(y) -> Printf.fprintf oc "\tneg\t%s, %s\n" y x 60 | | NonTail(x), Add(y, z') -> Printf.fprintf oc "\tadd\t%s, %s, %s\n" y (pp_id_or_imm z') x 61 | | NonTail(x), Sub(y, z') -> Printf.fprintf oc "\tsub\t%s, %s, %s\n" y (pp_id_or_imm z') x 62 | | NonTail(x), SLL(y, z') -> Printf.fprintf oc "\tsll\t%s, %s, %s\n" y (pp_id_or_imm z') x 63 | | NonTail(x), Ld(y, z') -> Printf.fprintf oc "\tld\t[%s + %s], %s\n" y (pp_id_or_imm z') x 64 | | NonTail(_), St(x, y, z') -> Printf.fprintf oc "\tst\t%s, [%s + %s]\n" x y (pp_id_or_imm z') 65 | | NonTail(x), FMovD(y) when x = y -> () 66 | | NonTail(x), FMovD(y) -> 67 | Printf.fprintf oc "\tfmovs\t%s, %s\n" y x; 68 | Printf.fprintf oc "\tfmovs\t%s, %s\n" (co_freg y) (co_freg x) 69 | | NonTail(x), FNegD(y) -> 70 | Printf.fprintf oc "\tfnegs\t%s, %s\n" y x; 71 | if x <> y then Printf.fprintf oc "\tfmovs\t%s, %s\n" (co_freg y) (co_freg x) 72 | | NonTail(x), FAddD(y, z) -> Printf.fprintf oc "\tfaddd\t%s, %s, %s\n" y z x 73 | | NonTail(x), FSubD(y, z) -> Printf.fprintf oc "\tfsubd\t%s, %s, %s\n" y z x 74 | | NonTail(x), FMulD(y, z) -> Printf.fprintf oc "\tfmuld\t%s, %s, %s\n" y z x 75 | | NonTail(x), FDivD(y, z) -> Printf.fprintf oc "\tfdivd\t%s, %s, %s\n" y z x 76 | | NonTail(x), LdDF(y, z') -> Printf.fprintf oc "\tldd\t[%s + %s], %s\n" y (pp_id_or_imm z') x 77 | | NonTail(_), StDF(x, y, z') -> Printf.fprintf oc "\tstd\t%s, [%s + %s]\n" x y (pp_id_or_imm z') 78 | | NonTail(_), Comment(s) -> Printf.fprintf oc "\t! %s\n" s 79 | (* 退避の仮想命令の実装 *) 80 | | NonTail(_), Save(x, y) when List.mem x allregs && not (S.mem y !stackset) -> 81 | save y; 82 | Printf.fprintf oc "\tst\t%s, [%s + %d]\n" x reg_sp (offset y) 83 | | NonTail(_), Save(x, y) when List.mem x allfregs && not (S.mem y !stackset) -> 84 | savef y; 85 | Printf.fprintf oc "\tstd\t%s, [%s + %d]\n" x reg_sp (offset y) 86 | | NonTail(_), Save(x, y) -> assert (S.mem y !stackset); () 87 | (* 復帰の仮想命令の実装 *) 88 | | NonTail(x), Restore(y) when List.mem x allregs -> 89 | Printf.fprintf oc "\tld\t[%s + %d], %s\n" reg_sp (offset y) x 90 | | NonTail(x), Restore(y) -> 91 | assert (List.mem x allfregs); 92 | Printf.fprintf oc "\tldd\t[%s + %d], %s\n" reg_sp (offset y) x 93 | (* 末尾だったら計算結果を第一レジスタにセットしてret *) 94 | | Tail, (Nop | St _ | StDF _ | Comment _ | Save _ as exp) -> 95 | g' oc (NonTail(Id.gentmp Type.Unit), exp); 96 | Printf.fprintf oc "\tretl\n"; 97 | Printf.fprintf oc "\tnop\n" 98 | | Tail, (Set _ | SetL _ | Mov _ | Neg _ | Add _ | Sub _ | SLL _ | Ld _ as exp) -> 99 | g' oc (NonTail(regs.(0)), exp); 100 | Printf.fprintf oc "\tretl\n"; 101 | Printf.fprintf oc "\tnop\n" 102 | | Tail, (FMovD _ | FNegD _ | FAddD _ | FSubD _ | FMulD _ | FDivD _ | LdDF _ as exp) -> 103 | g' oc (NonTail(fregs.(0)), exp); 104 | Printf.fprintf oc "\tretl\n"; 105 | Printf.fprintf oc "\tnop\n" 106 | | Tail, (Restore(x) as exp) -> 107 | (match locate x with 108 | | [i] -> g' oc (NonTail(regs.(0)), exp) 109 | | [i; j] when i + 1 = j -> g' oc (NonTail(fregs.(0)), exp) 110 | | _ -> assert false); 111 | Printf.fprintf oc "\tretl\n"; 112 | Printf.fprintf oc "\tnop\n" 113 | | Tail, IfEq(x, y', e1, e2) -> 114 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 115 | g'_tail_if oc e1 e2 "be" "bne" 116 | | Tail, IfLE(x, y', e1, e2) -> 117 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 118 | g'_tail_if oc e1 e2 "ble" "bg" 119 | | Tail, IfGE(x, y', e1, e2) -> 120 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 121 | g'_tail_if oc e1 e2 "bge" "bl" 122 | | Tail, IfFEq(x, y, e1, e2) -> 123 | Printf.fprintf oc "\tfcmpd\t%s, %s\n" x y; 124 | Printf.fprintf oc "\tnop\n"; 125 | g'_tail_if oc e1 e2 "fbe" "fbne" 126 | | Tail, IfFLE(x, y, e1, e2) -> 127 | Printf.fprintf oc "\tfcmpd\t%s, %s\n" x y; 128 | Printf.fprintf oc "\tnop\n"; 129 | g'_tail_if oc e1 e2 "fble" "fbg" 130 | | NonTail(z), IfEq(x, y', e1, e2) -> 131 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 132 | g'_non_tail_if oc (NonTail(z)) e1 e2 "be" "bne" 133 | | NonTail(z), IfLE(x, y', e1, e2) -> 134 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 135 | g'_non_tail_if oc (NonTail(z)) e1 e2 "ble" "bg" 136 | | NonTail(z), IfGE(x, y', e1, e2) -> 137 | Printf.fprintf oc "\tcmp\t%s, %s\n" x (pp_id_or_imm y'); 138 | g'_non_tail_if oc (NonTail(z)) e1 e2 "bge" "bl" 139 | | NonTail(z), IfFEq(x, y, e1, e2) -> 140 | Printf.fprintf oc "\tfcmpd\t%s, %s\n" x y; 141 | Printf.fprintf oc "\tnop\n"; 142 | g'_non_tail_if oc (NonTail(z)) e1 e2 "fbe" "fbne" 143 | | NonTail(z), IfFLE(x, y, e1, e2) -> 144 | Printf.fprintf oc "\tfcmpd\t%s, %s\n" x y; 145 | Printf.fprintf oc "\tnop\n"; 146 | g'_non_tail_if oc (NonTail(z)) e1 e2 "fble" "fbg" 147 | (* 関数呼び出しの仮想命令の実装 *) 148 | | Tail, CallCls(x, ys, zs) -> (* 末尾呼び出し *) 149 | g'_args oc [(x, reg_cl)] ys zs; 150 | Printf.fprintf oc "\tld\t[%s + 0], %s\n" reg_cl reg_sw; 151 | Printf.fprintf oc "\tjmp\t%s\n" reg_sw; 152 | Printf.fprintf oc "\tnop\n" 153 | | Tail, CallDir(Id.L(x), ys, zs) -> (* 末尾呼び出し *) 154 | g'_args oc [] ys zs; 155 | Printf.fprintf oc "\tb\t%s\n" x; 156 | Printf.fprintf oc "\tnop\n" 157 | | NonTail(a), CallCls(x, ys, zs) -> 158 | g'_args oc [(x, reg_cl)] ys zs; 159 | let ss = stacksize () in 160 | Printf.fprintf oc "\tst\t%s, [%s + %d]\n" reg_ra reg_sp (ss - 4); 161 | Printf.fprintf oc "\tld\t[%s + 0], %s\n" reg_cl reg_sw; 162 | Printf.fprintf oc "\tcall\t%s\n" reg_sw; 163 | Printf.fprintf oc "\tadd\t%s, %d, %s\t! delay slot\n" reg_sp ss reg_sp; 164 | Printf.fprintf oc "\tsub\t%s, %d, %s\n" reg_sp ss reg_sp; 165 | Printf.fprintf oc "\tld\t[%s + %d], %s\n" reg_sp (ss - 4) reg_ra; 166 | if List.mem a allregs && a <> regs.(0) then 167 | Printf.fprintf oc "\tmov\t%s, %s\n" regs.(0) a 168 | else if List.mem a allfregs && a <> fregs.(0) then 169 | (Printf.fprintf oc "\tfmovs\t%s, %s\n" fregs.(0) a; 170 | Printf.fprintf oc "\tfmovs\t%s, %s\n" (co_freg fregs.(0)) (co_freg a)) 171 | | NonTail(a), CallDir(Id.L(x), ys, zs) -> 172 | g'_args oc [] ys zs; 173 | let ss = stacksize () in 174 | Printf.fprintf oc "\tst\t%s, [%s + %d]\n" reg_ra reg_sp (ss - 4); 175 | Printf.fprintf oc "\tcall\t%s\n" x; 176 | Printf.fprintf oc "\tadd\t%s, %d, %s\t! delay slot\n" reg_sp ss reg_sp; 177 | Printf.fprintf oc "\tsub\t%s, %d, %s\n" reg_sp ss reg_sp; 178 | Printf.fprintf oc "\tld\t[%s + %d], %s\n" reg_sp (ss - 4) reg_ra; 179 | if List.mem a allregs && a <> regs.(0) then 180 | Printf.fprintf oc "\tmov\t%s, %s\n" regs.(0) a 181 | else if List.mem a allfregs && a <> fregs.(0) then 182 | (Printf.fprintf oc "\tfmovs\t%s, %s\n" fregs.(0) a; 183 | Printf.fprintf oc "\tfmovs\t%s, %s\n" (co_freg fregs.(0)) (co_freg a)) 184 | and g'_tail_if oc e1 e2 b bn = 185 | let b_else = Id.genid (b ^ "_else") in 186 | Printf.fprintf oc "\t%s\t%s\n" bn b_else; 187 | Printf.fprintf oc "\tnop\n"; 188 | let stackset_back = !stackset in 189 | g oc (Tail, e1); 190 | Printf.fprintf oc "%s:\n" b_else; 191 | stackset := stackset_back; 192 | g oc (Tail, e2) 193 | and g'_non_tail_if oc dest e1 e2 b bn = 194 | let b_else = Id.genid (b ^ "_else") in 195 | let b_cont = Id.genid (b ^ "_cont") in 196 | Printf.fprintf oc "\t%s\t%s\n" bn b_else; 197 | Printf.fprintf oc "\tnop\n"; 198 | let stackset_back = !stackset in 199 | g oc (dest, e1); 200 | let stackset1 = !stackset in 201 | Printf.fprintf oc "\tb\t%s\n" b_cont; 202 | Printf.fprintf oc "\tnop\n"; 203 | Printf.fprintf oc "%s:\n" b_else; 204 | stackset := stackset_back; 205 | g oc (dest, e2); 206 | Printf.fprintf oc "%s:\n" b_cont; 207 | let stackset2 = !stackset in 208 | stackset := S.inter stackset1 stackset2 209 | and g'_args oc x_reg_cl ys zs = 210 | let (i, yrs) = 211 | List.fold_left 212 | (fun (i, yrs) y -> (i + 1, (y, regs.(i)) :: yrs)) 213 | (0, x_reg_cl) 214 | ys in 215 | List.iter 216 | (fun (y, r) -> Printf.fprintf oc "\tmov\t%s, %s\n" y r) 217 | (shuffle reg_sw yrs); 218 | let (d, zfrs) = 219 | List.fold_left 220 | (fun (d, zfrs) z -> (d + 1, (z, fregs.(d)) :: zfrs)) 221 | (0, []) 222 | zs in 223 | List.iter 224 | (fun (z, fr) -> 225 | Printf.fprintf oc "\tfmovs\t%s, %s\n" z fr; 226 | Printf.fprintf oc "\tfmovs\t%s, %s\n" (co_freg z) (co_freg fr)) 227 | (shuffle reg_fsw zfrs) 228 | 229 | let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = 230 | Printf.fprintf oc "%s:\n" x; 231 | stackset := S.empty; 232 | stackmap := []; 233 | g oc (Tail, e) 234 | 235 | let f oc (Prog(data, fundefs, e)) = 236 | Format.eprintf "generating assembly...@."; 237 | Printf.fprintf oc ".section\t\".rodata\"\n"; 238 | Printf.fprintf oc ".align\t8\n"; 239 | List.iter 240 | (fun (Id.L(x), d) -> 241 | Printf.fprintf oc "%s:\t! %f\n" x d; 242 | Printf.fprintf oc "\t.long\t0x%lx\n" (gethi d); 243 | Printf.fprintf oc "\t.long\t0x%lx\n" (getlo d)) 244 | data; 245 | Printf.fprintf oc ".section\t\".text\"\n"; 246 | List.iter (fun fundef -> h oc fundef) fundefs; 247 | Printf.fprintf oc ".global\tmin_caml_start\n"; 248 | Printf.fprintf oc "min_caml_start:\n"; 249 | Printf.fprintf oc "\tsave\t%%sp, -112, %%sp\n"; (* from gcc; why 112? *) 250 | stackset := S.empty; 251 | stackmap := []; 252 | g oc (NonTail("%g0"), e); 253 | Printf.fprintf oc "\tret\n"; 254 | Printf.fprintf oc "\trestore\n" 255 | -------------------------------------------------------------------------------- /src/ulc/ulc.ml: -------------------------------------------------------------------------------- 1 | type term = 2 | | Var of string 3 | | Lam of string * term 4 | | App of term * term 5 | | Pair of term * term 6 | | Fst of term 7 | | Snd of term 8 | | Unit 9 | 10 | let rec string_of_term = function 11 | | Var x -> x 12 | | Lam (x, t) -> "λ" ^ x ^ ". " ^ string_of_term t 13 | | App (t1, t2) -> "(" ^ string_of_term t1 ^ " " ^ string_of_term t2 ^ ")" 14 | | Pair (t1, t2) -> "<" ^ string_of_term t1 ^ ", " ^ string_of_term t2 ^ ">" 15 | | Fst t -> "fst " ^ string_of_term t 16 | | Snd t -> "snd " ^ string_of_term t 17 | | Unit -> "unit" 18 | 19 | let rec subst x s = function 20 | | Var y -> if x = y then s else Var y 21 | | Lam (y, t) when x <> y -> Lam (y, subst x s t) 22 | | App (f, a) -> App (subst x s f, subst x s a) 23 | | Pair (t1, t2) -> Pair (subst x s t1, subst x s t2) 24 | | Fst t -> Fst (subst x s t) 25 | | Snd t -> Snd (subst x s t) 26 | | Unit -> Unit 27 | | t -> t 28 | 29 | let rec equal t1 t2 = 30 | match t1, t2 with 31 | | Var x, Var y -> x = y 32 | | Lam (x, b), Lam (y, b') -> equal b (subst y (Var x) b') 33 | | Lam (x, b), t -> equal b (App (t, Var x)) 34 | | t, Lam (x, b) -> equal (App (t, Var x)) b 35 | | App (f1, a1), App (f2, a2) -> equal f1 f2 && equal a1 a2 36 | | Pair (t1, t2), Pair (t1', t2') -> equal t1 t1' && equal t2 t2' 37 | | Fst t, Fst t' -> equal t t' 38 | | Snd t, Snd t' -> equal t t' 39 | | Unit, Unit -> true 40 | | _ -> false 41 | 42 | let rec reduce = function 43 | | App (Lam (x, b), a) -> subst x a b 44 | | App (f, a) -> App (reduce f, reduce a) 45 | | Pair (t1, t2) -> Pair (reduce t1, reduce t2) 46 | | Fst (Pair (t1, t2)) -> t1 47 | | Fst t -> Fst (reduce t) 48 | | Snd (Pair (t1, t2)) -> t2 49 | | Snd t -> Snd (reduce t) 50 | | Unit -> Unit 51 | | t -> t 52 | 53 | let rec normalize t = 54 | let t' = reduce t in 55 | if equal t t' then t else normalize t' 56 | 57 | let id = Lam ("x", Var "x") 58 | let const = Lam ("x", Lam ("y", Var "x")) 59 | let one = Lam ("f", Lam ("x", App (Var "f", Var "x"))) 60 | let two = Lam ("f", Lam ("x", App (Var "f", App (Var "f", Var "x")))) 61 | 62 | let beta = (App (Lam ("x", Var "x"), Var "y"), Var "y") 63 | let eta = (Lam ("x", App (Var "f", Var "x")), Var "f") 64 | let eta_domain = (Lam ("x", App (Var "f", Var "x")), Var "f") 65 | let invalid_eta = (Lam ("x", Var "z"), Var "z") 66 | let invalid_eta_v = (Lam ("x", Var "u"), Var "u") 67 | let pair_test = (Fst (Pair (Var "x", Var "y")), Var "x") 68 | let snd_test = (Snd (Pair (Var "x", Var "y")), Var "y") 69 | let unit_test = (App (Lam ("x", Unit), Var "z"), Unit) 70 | 71 | let test_equal name (t1, t2) = 72 | let t1' = normalize t1 in 73 | let t2' = normalize t2 in 74 | let result = equal t1' t2' in 75 | Printf.printf "Test %s:\n- Term1: %s\n- Term2: %s\n- Result: %s\n\n" 76 | name 77 | (string_of_term t1) 78 | (string_of_term t2) 79 | (if result then "PASS" else "FAIL") 80 | 81 | let () = 82 | test_equal "Beta" beta; 83 | test_equal "Eta" eta; 84 | test_equal "Eta Domain" eta_domain; 85 | test_equal "Invalid Eta" invalid_eta; 86 | test_equal "Invalid Eta Var" invalid_eta_v; 87 | test_equal "Pair Fst" pair_test; 88 | test_equal "Pair Snd" snd_test; 89 | test_equal "Unit" unit_test; 90 | -------------------------------------------------------------------------------- /src/vm/config.ml: -------------------------------------------------------------------------------- 1 | let sh_flg = ref false 2 | let vm_debug_flg = ref false 3 | let tail_opt_flg = ref true 4 | let frame_reset_flg = ref true 5 | let stack_mode_flg : [ `User_stack | `Host_stack ] ref = ref `User_stack 6 | -------------------------------------------------------------------------------- /src/vm/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name BacCaml) 3 | (public_name vm.lib) 4 | (modules (:standard \ main)) 5 | (flags (-w -4-33-40-41)) 6 | (libraries str MinCaml) 7 | (foreign_stubs (language c) (names libmincaml)) 8 | (foreign_stubs (language c) (names float)) 9 | (preprocess (pps ppx_deriving.show ppx_deriving.enum))) 10 | 11 | (executable 12 | (name main) 13 | (public_name vm) 14 | (flags (-w -4-33-40-41)) 15 | (modules Main) 16 | (package vm) 17 | (libraries MinCaml BacCaml)) 18 | -------------------------------------------------------------------------------- /src/vm/emit.ml: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | open Asm 3 | open Config 4 | open Insts 5 | module List = ListLabels 6 | 7 | exception Error of string 8 | 9 | (* generate a unique label id *) 10 | let gen_label, reset = 11 | let counter = ref 0 in 12 | ( (fun () -> 13 | let l = !counter in 14 | counter := !counter + 1; 15 | "$" ^ string_of_int l) 16 | , fun () -> counter := 0 ) 17 | 18 | (* compilation environment maps local variable names to local variable 19 | numbers *) 20 | let lookup env var = 21 | match 22 | List.find_opt ~f:(fun (_, v) -> var = v) (List.mapi ~f:(fun idx v -> idx, v) env) 23 | with 24 | | Some v -> fst v 25 | | None -> failwith (Printf.sprintf "%s not found" var) 26 | 27 | let extend_env env var = var :: env 28 | let shift_env env = extend_env env "*dummy*" 29 | let downshift_env env = List.tl env 30 | let return_address_marker = "$ret_addr" 31 | let jit_flg_marker = "$jit_flg" 32 | 33 | let build_arg_env args = 34 | match !sh_flg with 35 | | true -> return_address_marker :: jit_flg_marker :: List.rev args 36 | | false -> return_address_marker :: List.rev args 37 | 38 | (* computes the number of arguments to this frame. The stack has a shape like 39 | [...local vars...][ret addr][..args...], the return address position from the 40 | top indicates the number of local variables on top of the return address. *) 41 | let arity_of_env env = 42 | let num_local_vars = lookup env return_address_marker in 43 | List.length env - num_local_vars - 1, num_local_vars 44 | 45 | let arity_of_env_sh env = 46 | let num_local_vars = lookup env return_address_marker in 47 | let flg_offset = lookup env return_address_marker in 48 | List.length env - num_local_vars - 1, num_local_vars, flg_offset 49 | 50 | let label_counter = ref 0 51 | 52 | let gen_label _ = 53 | let l = !label_counter in 54 | label_counter := l + 1; 55 | "$" ^ string_of_int l 56 | 57 | let reset _ = label_counter := 0 58 | 59 | let compile_id_or_imm env = function 60 | | Asm.C n -> if n = 0 then [ CONST0 ] else [ CONST; Literal n ] 61 | | Asm.V x -> let y = lookup env x in if y = 0 then [ DUP0 ] else [ DUP; Literal y ] 62 | 63 | let rec compile_t fname env = 64 | let open Asm in 65 | function 66 | | Ans (CallDir (Id.L fname', args, fargs) as e) -> 67 | if not @@ !Config.tail_opt_flg 68 | then compile_exp fname env e 69 | else if fname' = fname 70 | then ( 71 | let old_arity, local_size = arity_of_env env in 72 | let new_arity = List.length args in 73 | (List.fold_left 74 | ~f:(fun (rev_code_list, env) v -> 75 | compile_id_or_imm env (V v) :: rev_code_list, shift_env env) 76 | ~init:([], env) 77 | args 78 | |> fst 79 | |> List.rev 80 | |> List.flatten) 81 | @ (if !Config.frame_reset_flg 82 | then 83 | [ FRAME_RESET 84 | ; Literal old_arity 85 | ; Literal local_size 86 | ; Literal new_arity 87 | ] 88 | else []) 89 | @ [ JUMP; Lref fname ]) 90 | else compile_exp fname env e 91 | | Ans e -> compile_exp fname env e 92 | | Let ((x, _), exp, t) -> 93 | let ex_env = extend_env env x in 94 | compile_exp fname env exp @ compile_t fname ex_env t @ [ POP1 ] 95 | 96 | and compile_exp fname env exp = 97 | let open Asm in 98 | match exp with 99 | | Nop -> [] 100 | | Li i -> compile_id_or_imm env (C i) 101 | | Mov var -> compile_id_or_imm env (V var) 102 | | Neg var -> compile_id_or_imm env (V var) @ [ NEG ] 103 | | Add (x, y) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ ADD ] 104 | | Sub (x, y) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ SUB ] 105 | | Mul (x, y) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ MUL ] 106 | | Div (x, y) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ DIV ] 107 | | Mod (x, y) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ MOD ] 108 | | IfEq (x, y, then_exp, else_exp) -> 109 | let l2, l1 = gen_label (), gen_label () in compile_id_or_imm env (V x) 110 | @ compile_id_or_imm (shift_env env) y @ [ EQ ] @ [ JUMP_IF_ZERO; Lref l1 ] 111 | @ compile_t fname env then_exp @ [ JUMP; Lref l2 ] @ [ Ldef l1 ] 112 | @ compile_t fname env else_exp @ [ Ldef l2 ] 113 | | IfLE (x, y, then_exp, else_exp) -> 114 | let l2, l1 = gen_label (), gen_label () in compile_id_or_imm env (V x) 115 | @ compile_id_or_imm (shift_env env) y @ [ LT ] @ [ JUMP_IF_ZERO; Lref l1 ] 116 | @ compile_t fname env then_exp @ [ JUMP; Lref l2 ] @ [ Ldef l1 ] 117 | @ compile_t fname env else_exp @ [ Ldef l2 ] 118 | | IfGE (x, y, then_exp, else_exp) -> 119 | let l2, l1 = gen_label (), gen_label () in compile_id_or_imm env (V x) 120 | @ compile_id_or_imm (shift_env env) y @ [ GT ] @ [ JUMP_IF_ZERO; Lref l1 ] 121 | @ compile_t fname env then_exp @ [ JUMP; Lref l2 ] @ [ Ldef l1 ] 122 | @ compile_t fname env else_exp @ [ Ldef l2 ] 123 | | CallDir (Id.L "min_caml_mul", [ x; y ], _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ [ MUL ] 124 | | CallDir (Id.L "min_caml_div", [ x; y ], _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ [ DIV ] 125 | | CallDir (Id.L "min_caml_rem", [ x; y ], _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ [ MOD ] 126 | | CallDir (Id.L "min_caml_read_int", _, _) -> [ READ_INT ] 127 | | CallDir (Id.L "min_caml_print_int", [ x ], _) -> compile_id_or_imm env (V x) @ [ PRINT_INT ] 128 | | CallDir (Id.L "min_caml_read_string", _, _) -> [ READ_STRING ] 129 | | CallDir (Id.L "min_caml_print_string", [x], _) -> compile_id_or_imm env (V x) @ [ PRINT_STRING ] 130 | | CallDir (Id.L "min_caml_print_newline", _, _) -> [ PRINT_NEWLINE ] 131 | | CallDir (Id.L "min_caml_rand_int", [ x ], _) -> compile_id_or_imm env (V x) @ [ RAND_INT ] 132 | | CallDir (Id.L "min_caml_create_array", [ x; y ], _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ [ ARRAY_MAKE ] 133 | | CallDir (Id.L var, rands, _) -> 134 | ( List.fold_left ~f:(fun (rev_code_list, env) v -> compile_id_or_imm env (V v) :: 135 | rev_code_list, shift_env env) ~init:([], env) rands |> fst |> List.rev |> List.flatten) 136 | @ (if fname = "main" then [ JIT_SETUP ] else []) @ [ CALL; Lref var; Literal (List.length rands) ] 137 | | Ld (x, y, _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) y @ [ GET ] 138 | | St (x, y, z, _) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ compile_id_or_imm (shift_env (shift_env env)) z @ [ PUT ] 139 | | Stw (x, y, z) -> compile_id_or_imm env (V x) @ compile_id_or_imm (shift_env env) (V y) @ compile_id_or_imm (shift_env (shift_env env)) z @ [ PUT ] 140 | | exp -> failwith (Printf.sprintf "un matched pattern: %s" (Asm.show_exp exp)) 141 | ;; 142 | 143 | let rec assoc_tail_rec fname = function 144 | | Ans (CallDir (Id.L fname', args, fargs)) -> fname = fname' 145 | | Ans e -> assoc_tail_rec' fname e 146 | | Let (_, _, t) -> assoc_tail_rec fname t 147 | 148 | and assoc_tail_rec' fname = function 149 | | IfEq (_, _, e1, e2) 150 | | IfLE (_, _, e1, e2) 151 | | IfGE (_, _, e1, e2) 152 | | IfFEq (_, _, e1, e2) 153 | | IfFLE (_, _, e1, e2) -> 154 | assoc_tail_rec fname e1 || assoc_tail_rec fname e2 155 | | e -> false 156 | ;; 157 | 158 | (* resolving labels *) 159 | let assoc_if subst elm = try List.assoc elm subst with Not_found -> elm 160 | 161 | (* [...;Ldef a;...] -> [...;a,i;...] where i is the index of the next 162 | instruction of Ldef a in the list all Ldefs are removed e.g., [_;Ldef 163 | 8;_;Ldef 7;_] ==> [8,1; 7,2] *) 164 | let make_label_env instrs = 165 | snd 166 | (List.fold_left 167 | ~f: 168 | (fun (addr, env) -> function 169 | | Ldef n -> addr, (Lref n, Literal addr) :: env 170 | | _ -> addr + 1, env) 171 | ~init:(0, []) 172 | instrs) 173 | ;; 174 | 175 | (* remove all Ldefs and replace Lrefs with Literals *) 176 | let resolve_labels instrs = 177 | let lenv = make_label_env instrs in 178 | instrs 179 | |> List.map ~f:(assoc_if lenv) 180 | |> List.filter ~f:(function Ldef _ -> false | _ -> true) 181 | ;; 182 | 183 | let compile_fun_body fenv name arity annot exp env = 184 | (match annot with 185 | (* | Some `TJ -> [ TRACING_COMP ] 186 | | Some `MJ -> [ METHOD_COMP ] *) 187 | | Some _ -> [] 188 | | None -> []) @ [ Ldef name ] @ compile_t name env exp @ if name = "main" then [ HALT ] else [ RET; Literal arity ] 189 | 190 | let compile_fun 191 | (fenv : Id.l -> Asm.fundef) 192 | Asm.{ name = Id.L name; args; body; annot } 193 | = 194 | compile_fun_body fenv name (List.length args) annot body (build_arg_env args) 195 | ;; 196 | 197 | let compile_funs fundefs = 198 | (* let fenv name = fst(List.find (fun (_,{name=n}) -> name=n) 199 | * (List.mapi (fun idx fdef -> (idx,fdef)) 200 | * fundefs)) in *) 201 | let fenv name = List.find ~f:(fun Asm.{ name = n } -> n = name) fundefs in 202 | Array.of_list 203 | (resolve_labels (List.flatten (List.map ~f:(compile_fun fenv) fundefs))) 204 | ;; 205 | 206 | let resolve_labels' instrs = 207 | List.fold_left ~init:(0, []) ~f:(fun (addr, env) -> function 208 | | Ldef n -> addr, (Lref n, Literal addr) :: env | _ -> addr + 1, env) 209 | ;; 210 | 211 | let f (Asm.Prog (_, fundefs, main)) = 212 | let open Asm in 213 | let main = 214 | { name = Id.L "main" 215 | ; args = [] 216 | ; fargs = [] 217 | ; ret = Type.Int 218 | ; body = main 219 | ; annot = None 220 | } 221 | in 222 | compile_funs (main :: fundefs) 223 | ;; 224 | -------------------------------------------------------------------------------- /src/vm/emit.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | open Insts 3 | 4 | val f : Asm.prog -> inst array 5 | -------------------------------------------------------------------------------- /src/vm/float.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef union { 7 | int32_t i[2]; 8 | double d; 9 | } dbl; 10 | 11 | value gethi(value v) { 12 | dbl d; 13 | d.d = Double_val(v); 14 | return caml_copy_int32(d.i[0]); 15 | } 16 | 17 | value getlo(value v) { 18 | dbl d; 19 | d.d = Double_val(v); 20 | return caml_copy_int32(d.i[1]); 21 | } 22 | -------------------------------------------------------------------------------- /src/vm/insts.ml: -------------------------------------------------------------------------------- 1 | 2 | type inst = 3 | | UNIT (* terminator *) 4 | | ADD | SUB | MUL | DIV | MOD | NOT | NEG (* binary and ALU ops *) 5 | | LT | GT | EQ (* equality *) 6 | | JUMP_IF_ZERO | JUMP | CALL | RET | HALT (* control flow *) 7 | | DUP | DUP0 | POP0 | POP1 (* stack ops *) 8 | | CONST0 | CONST (* constants *) 9 | | GET | PUT | ARRAY_MAKE (* vectors *) 10 | | FRAME_RESET | JIT_SETUP (* o l n *) 11 | | RAND_INT | READ_INT | READ_STRING (* io read *) 12 | | PRINT_INT | PRINT_NEWLINE | PRINT_STRING (* io write *) 13 | | Literal of int 14 | | Lref of string 15 | | Ldef of string [@@deriving show] 16 | 17 | let index_of instr = match instr with 18 | | UNIT -> 0 | ADD -> 1 | SUB -> 2 | MUL -> 3 19 | | DIV -> 4 | MOD -> 5 | NOT -> 6 | NEG -> 7 20 | | LT -> 8 | GT -> 9 | EQ -> 10 | HALT -> 11 21 | | JUMP_IF_ZERO -> 12 | JUMP -> 13 | CALL -> 14 | RET -> 15 22 | | DUP -> 16 | DUP0 -> 17 | POP0 -> 18 | POP1 -> 19 | CONST0 -> 20 23 | | CONST -> 21 | GET -> 22 | PUT -> 23 | ARRAY_MAKE -> 24 24 | | FRAME_RESET -> 25 | JIT_SETUP -> 26 25 | | RAND_INT -> 27 | READ_INT -> 28 | READ_STRING -> 29 26 | | PRINT_INT -> 30 | PRINT_NEWLINE -> 31 | PRINT_STRING -> 32 27 | | _ -> 33 28 | 29 | let instsmap = 30 | [| UNIT ; ADD ; SUB ; MUL ; DIV ; MOD ; NOT ; NEG 31 | ; LT ; GT ; EQ ; JUMP_IF_ZERO ; JUMP ; CALL ; RET ; HALT 32 | ; DUP ; DUP0 ; POP0 ; POP1 ; CONST0 ; CONST 33 | ; GET ; PUT ; ARRAY_MAKE ; FRAME_RESET ; JIT_SETUP 34 | ; RAND_INT ; READ_INT ; READ_STRING 35 | ; PRINT_INT ; PRINT_NEWLINE ; PRINT_STRING 36 | |] 37 | 38 | module Printer = struct 39 | let pp_inst_map () = 40 | ignore 41 | (Array.fold_left 42 | (fun i instr -> 43 | Printf.printf "%s => %d\n" (show_inst instr) i; 44 | i + 1) 45 | 0 46 | instsmap) 47 | ;; 48 | 49 | let pp_insts_counter = ref 0 50 | 51 | let pp_pc () = 52 | print_int !pp_insts_counter; 53 | print_string "\t"; 54 | incr pp_insts_counter 55 | ;; 56 | 57 | let rec pp_insts ?(i = 0) insts = 58 | match insts with 59 | | [] -> () 60 | | hd :: tl -> 61 | (match hd with 62 | | CONST | DUP | JUMP | JUMP_IF_ZERO | RET -> 63 | pp_pc (); 64 | print_string (show_inst hd); 65 | print_string " "; 66 | pp_insts ~i:0 tl 67 | | CALL -> 68 | pp_pc (); 69 | print_string (show_inst hd); 70 | print_string " "; 71 | pp_insts ~i:1 tl 72 | | Literal n -> 73 | print_string "\t"; 74 | print_string (show_inst hd); 75 | if i = 0 then print_newline () else print_string "\t"; 76 | incr pp_insts_counter; 77 | pp_insts ~i:(i - 1) tl 78 | | _ -> 79 | pp_pc (); 80 | print_string (show_inst hd); 81 | print_newline (); 82 | pp_insts ~i:0 tl) 83 | ;; 84 | 85 | let rec pp_bytecode oc insts = 86 | insts 87 | |> Array.mapi (fun i x -> i, x) 88 | |> Array.map (fun (i, instr) -> 89 | match instr with 90 | | CONST | DUP | JUMP | JUMP_IF_ZERO | CALL | RET -> 91 | Printf.fprintf oc "code.(%d) <- %d; " i (index_of instr) 92 | | Literal j -> Printf.fprintf oc "code.(%d) <- %d;\n" i j 93 | | _ -> Printf.fprintf oc "code.(%d) <- %d;\n" i (index_of instr)) 94 | |> ignore 95 | ;; 96 | 97 | let write_bytecode oc insts = 98 | Printf.fprintf oc "%d\n" (Array.length insts); 99 | insts 100 | |> Array.map (fun instr -> 101 | match instr with 102 | | Literal j -> Printf.fprintf oc "%d\n" j 103 | | _ -> Printf.fprintf oc "%d\n" (index_of instr)) 104 | |> ignore 105 | ;; 106 | end 107 | -------------------------------------------------------------------------------- /src/vm/insts.mli: -------------------------------------------------------------------------------- 1 | type inst = 2 | | UNIT (* terminator *) 3 | | ADD | SUB | MUL | DIV | MOD | NOT | NEG (* binary and ALU ops *) 4 | | LT | GT | EQ (* equality *) 5 | | JUMP_IF_ZERO | JUMP | CALL | RET | HALT (* control flow *) 6 | | DUP | DUP0 | POP0 | POP1 (* stack ops *) 7 | | CONST0 | CONST (* constants *) 8 | | GET | PUT | ARRAY_MAKE (* vectors *) 9 | | FRAME_RESET | JIT_SETUP (* o l n *) 10 | | RAND_INT | READ_INT | READ_STRING (* io read *) 11 | | PRINT_INT | PRINT_NEWLINE | PRINT_STRING (* io write *) 12 | | Literal of int 13 | | Lref of string 14 | | Ldef of string 15 | 16 | val index_of : inst -> int 17 | val show_inst : inst -> string 18 | val instsmap : inst array 19 | 20 | module Printer : sig 21 | val pp_inst_map : unit -> unit 22 | val pp_insts : ?i:int -> inst list -> unit 23 | val pp_bytecode : out_channel -> inst array -> unit 24 | val write_bytecode : out_channel -> inst array -> unit 25 | end 26 | -------------------------------------------------------------------------------- /src/vm/libmincaml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NOTE: コンパイル後のシンボルのプリフィックスに _ がつくのを避ける 5 | void min_caml_print_int(long n) asm("min_caml_print_int"); 6 | void min_caml_print_newline() asm("min_caml_print_newline"); 7 | long* min_caml_create_array(long number_of_element, long init_value) asm("min_caml_create_array"); 8 | double* min_caml_create_float_array(long number_of_element, double float_value) asm("min_caml_create_float_array"); 9 | long min_caml_truncate(double d) asm("min_caml_truncate"); 10 | void min_caml_print_float(double d) asm("min_caml_print_float"); 11 | void min_caml_print_byte(long n) asm("min_caml_print_byte"); 12 | long min_caml_read_int() asm("min_caml_read_int"); 13 | long min_caml_mul(long x, long y) asm("min_caml_mul"); 14 | long min_caml_div(long x, long y) asm("min_caml_div"); 15 | long min_caml_rem(long x, long y) asm("min_caml_rem"); 16 | double min_caml_read_float() asm("min_caml_read_float"); 17 | double min_caml_atan(double x) asm("min_caml_atan"); 18 | double min_caml_cos(double x) asm("min_caml_cos"); 19 | double min_caml_floor(double x) asm("min_caml_floor"); 20 | double min_caml_sin(double x) asm("min_caml_sin"); 21 | double min_caml_abs_float(double x) asm("min_caml_abs_float"); 22 | double min_caml_float_of_int(long n) asm("min_caml_float_of_int"); 23 | long min_caml_int_of_float(double d) asm("min_caml_int_of_float"); 24 | double min_caml_sqrt(double d) asm("min_caml_sqrt"); 25 | 26 | void min_caml_print_int(long n) { 27 | printf("%ld", n); 28 | } 29 | 30 | void min_caml_print_newline() { 31 | printf("\n"); 32 | } 33 | 34 | long* min_caml_create_array(long number_of_element, long init_value) { 35 | long *heap_ptr; 36 | 37 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 38 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 39 | 40 | // Array の先頭アドレスを取得 41 | long *array_ptr = heap_ptr; 42 | 43 | for (long i = 0l; i < number_of_element; i++) { 44 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 45 | *heap_ptr = init_value; 46 | heap_ptr += 1; 47 | } 48 | 49 | // ヒープの先頭アドレスを x27 に書き戻す 50 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 51 | 52 | return array_ptr; 53 | } 54 | 55 | double* min_caml_create_float_array(long number_of_element, double float_value) { 56 | double *heap_ptr; 57 | 58 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 59 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 60 | 61 | // Array の先頭アドレスを取得 62 | double *array_ptr = heap_ptr; 63 | 64 | for (long i = 0l; i < number_of_element; i++) { 65 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 66 | *heap_ptr = float_value; 67 | heap_ptr += 1; 68 | } 69 | 70 | // ヒープの先頭アドレスを x27 に書き戻す 71 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 72 | 73 | return array_ptr; 74 | } 75 | 76 | // truncate 77 | long min_caml_truncate(double d) { 78 | return (long)d; 79 | } 80 | 81 | void min_caml_print_float(double d) { 82 | printf("%lf", d); 83 | } 84 | 85 | void min_caml_print_byte(long n) { 86 | putchar(n); 87 | } 88 | 89 | long min_caml_read_int() { 90 | long l; 91 | // fscanf(fp, "%ld", &l); 92 | scanf("%ld", &l); 93 | return l; 94 | } 95 | 96 | double min_caml_read_float() { 97 | double d; 98 | // fscanf(fp, "%lf", &d); 99 | scanf("%lf", &d); 100 | return d; 101 | } 102 | 103 | // atan 104 | double min_caml_atan(double x) { 105 | return atan(x); 106 | } 107 | 108 | // cos 109 | double min_caml_cos(double x) { 110 | return cos(x); 111 | } 112 | 113 | // floor 114 | double min_caml_floor(double x) { 115 | return floor(x); 116 | } 117 | 118 | // sin 119 | double min_caml_sin(double x) { 120 | return sin(x); 121 | } 122 | 123 | // abs_float 124 | double min_caml_abs_float(double x) { 125 | return fabs(x); 126 | } 127 | 128 | // float_of_int 129 | double min_caml_float_of_int(long n) { 130 | return (double)n; 131 | } 132 | 133 | // int_of_float 134 | long min_caml_int_of_float(double d) { 135 | return (long)d; 136 | } 137 | 138 | long min_caml_div(long x, long y) { 139 | return (long)(x/y); 140 | } 141 | 142 | long min_caml_rem(long x, long y) { 143 | return x % y; 144 | } 145 | 146 | long min_caml_mul(long x, long y) { 147 | return x * y; 148 | } 149 | 150 | // sqrt 151 | double min_caml_sqrt(double d) { 152 | return sqrt(d); 153 | } 154 | -------------------------------------------------------------------------------- /src/vm/main.ml: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | open Stdlib 3 | open Printf 4 | 5 | type backend = Compile | Interpret | Nothing 6 | 7 | let backend_type = ref Compile 8 | let show_insts_map_type = ref false 9 | let debug_flg = ref false 10 | 11 | let with_debug f = 12 | match !debug_flg with 13 | | true -> BacCaml.Config.vm_debug_flg := true; f () 14 | | false -> f () 15 | 16 | let with_show_insts f = 17 | match !show_insts_map_type with 18 | | true -> BacCaml.Insts.Printer.pp_inst_map () 19 | | false -> f () 20 | 21 | let rec normalForm l = 22 | let open BacCaml in 23 | let expression = Parser.exp Lexer.token l in 24 | expression |> Typing.f 25 | |> KNormal.f 26 | |> Alpha.f 27 | |> Util.(iter !limit) 28 | |> Closure.f 29 | |> Virtual.f 30 | |> Simm.f 31 | 32 | let compileSource ml = 33 | let input = Lexing.from_channel ml in 34 | let res = normalForm input in 35 | let _ = close_in ml in res 36 | 37 | let main f = 38 | let r x = match x with 39 | | BacCaml.Insts.Literal i -> Printf.printf "%d;" i 40 | | _ -> Printf.printf "%d;" (BacCaml.Insts.index_of x) in 41 | Id.counter := 0; 42 | Typing.extenv := M.empty; 43 | try match !backend_type with 44 | | Interpret -> 45 | let joe = open_in_bin ((Filename.remove_extension f) ^ ".joe") in 46 | let insts = (Marshal.from_channel joe) in BacCaml.VM.run_asm insts ; close_in joe 47 | | Compile -> 48 | let ml = open_in ((Filename.remove_extension f) ^ ".ml") in 49 | let vm = open_out_gen [Open_binary;Open_wronly;Open_creat] 0o644 ((Filename.remove_extension f) ^ ".joe") in 50 | let insts = (BacCaml.Emit.f (compileSource ml)) in 51 | Stdlib.output_bytes vm (Marshal.to_bytes insts [Marshal.No_sharing]) ; close_out vm 52 | | Nothing -> () 53 | with | Invalid_argument _ -> () 54 | | e -> raise e 55 | 56 | let () = 57 | let files = ref [] in 58 | BacCaml.( 59 | Arg.parse 60 | [ ( "-debug", Arg.Unit (fun _ -> debug_flg := true), "run as debug mode" ) ; 61 | ( "-compile", Arg.Unit (fun _ -> backend_type := Compile), "emit MinCaml IR" ) ; 62 | ( "-exec", Arg.Unit (fun _ -> backend_type := Interpret), "run IR in VM interpreter" ) ; 63 | ( "-no-tail", Arg.Unit (fun _ -> Config.tail_opt_flg := false), "disable optimization for tail-recursive call") ; 64 | ( "-no-fr", Arg.Unit (fun _ -> Config.frame_reset_flg := true), "disable to emit frame_reset" ) 65 | ]) 66 | (fun s -> files := !files @ [ s ]) 67 | ( "MinCaml IR Virtual Machine (c) 2024 Namdak Tonpa\n" 68 | ^ "usage: vm [-options] filenames"); 69 | with_show_insts (fun _ -> with_debug (fun _ -> List.iter main !files)) 70 | 71 | -------------------------------------------------------------------------------- /src/vm/vM.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open MinCaml 3 | open Config 4 | open Insts 5 | open Stdlib 6 | 7 | let max_stack_depth = 100000 8 | 9 | type fundef_bin_t = int array (* run the given program by calling the function id 0 *) 10 | type fundef_asm_t = inst array (* convert the given program into binary, and then run *) 11 | 12 | let with_debug f = match !vm_debug_flg with true -> f () | false -> () 13 | 14 | let index_of element array = 15 | fst (List.find 16 | (fun (_, v) -> v = element) 17 | (List.mapi (fun idx v -> idx, v) (Array.to_list array))) 18 | 19 | let int_of_inst = function 20 | | Literal n -> n 21 | | Ldef lbl | Lref lbl -> failwith ("unresolved " ^ lbl) 22 | | inst -> index_of inst instsmap 23 | 24 | let string_of = function 25 | | Literal n -> Printf.sprintf "Literal %d" n 26 | | Ldef n -> Printf.sprintf "Ldef %s" n 27 | | Lref n -> Printf.sprintf "Lref %s" n 28 | | i -> string_of_int (int_of_inst i) 29 | 30 | (* operand stack 31 | 32 | We pair the stack pointer and the array of values. Though the reference to 33 | the arry is not changed over the push/pop operations, those operations return 34 | a new pair just for convenience. If we switch to the MinCaml implementation, 35 | we should decouple the pair. *) 36 | 37 | type value = 38 | | Int' of int 39 | | String' of string 40 | | Array' of value array [@@deriving show] 41 | 42 | type stack = int * value array 43 | let push : stack -> value -> stack = fun (sp, stack) v -> stack.(sp) <- v ; sp + 1, stack 44 | let pop : stack -> value * stack = fun (sp, stack) -> stack.(sp - 1), (sp - 1, stack) 45 | let take : stack -> int -> value = fun (sp, stack) n -> stack.(sp - n - 1) 46 | let drop : stack -> int -> stack = fun (sp, stack) n -> sp - n, stack 47 | 48 | module Value = struct 49 | let ( |+| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> Int' (i + j) | _ -> failwith "invalid value" 50 | let ( |-| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> Int' (i - j) | _ -> failwith "invalid value" 51 | let ( |*| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> Int' (i * j) | _ -> failwith "invalid value" 52 | let ( |/| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> Int' (i / j) | _ -> failwith "invalid value" 53 | let ( |%| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> Int' (i mod j) | _ -> failwith "invalid_value" 54 | let ( |<| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> i < j | _ -> failwith "invalid value" 55 | let ( |=| ) v1 v2 = match v1, v2 with | Int' i, Int' j -> i = j | _ -> failwith "invalid value" 56 | 57 | let int_of_value = function 58 | | Int' i -> i 59 | | String' _ -> failwith "string is not int" 60 | | Array' _ -> failwith "array is not int" 61 | 62 | let string_of_value = function 63 | | Int' i -> failwith (sprintf "int %d is not string" i) 64 | | String' s -> s 65 | | Array' arr -> failwith (sprintf "array is not string") 66 | 67 | let array_of_value = function 68 | | Int' i -> failwith (sprintf "int %d is not array" i) 69 | | String' s -> failwith (sprintf "string %s is not array" s) 70 | | Array' arr -> arr 71 | 72 | let value_of_int i = Int' i 73 | let value_of_array arr = Array' arr 74 | let value_of_string s = String' s 75 | end 76 | 77 | 78 | let frame_reset : stack -> int -> int -> int -> stack = 79 | fun (sp, stack) o l n -> 80 | let ret = stack.(sp - n - l - 1) in 81 | (* save return address *) 82 | let jit_flg = stack.(sp - n - l - 2) in 83 | let old_base = sp - n - l - o - 1 in 84 | let new_base = sp - n in 85 | let _ = 86 | with_debug (fun () -> 87 | eprintf 88 | "offset: %d ret: %d, sp: %d, old_base: %d, new_base: %d\n" 89 | (sp - n - l - 1) 90 | (Value.int_of_value ret) 91 | sp 92 | old_base 93 | new_base) 94 | in 95 | let rec loop i = 96 | if n = i 97 | then ( 98 | match !sh_flg with 99 | | true -> 100 | stack.(old_base + n) <- jit_flg; 101 | stack.(old_base + (n + 1)) <- ret; 102 | old_base + n + 2, stack 103 | | false -> 104 | stack.(old_base + n) <- ret; 105 | old_base + n + 1, stack) 106 | else ( 107 | stack.(old_base + i) <- stack.(new_base + i); 108 | loop (i + 1)) 109 | in 110 | loop 0 111 | 112 | let make_stack () = 0, Array.make max_stack_depth (Int' 0) 113 | 114 | (* let test_stack =(9, [|1;2;3;4;5;6;7;8;9|]) 115 | * let reset_result = frame_reset test_stack 2 2 3 116 | * let _ = Printf.printf "reset -> [%s]\n" 117 | * (String.concat ";" 118 | * (List.map string_of_int (Array.to_list (snd reset_result)))) 119 | * let _ = assert ((5, [|1;7;8;9;4;6;7;8;9|]) = reset_result) *) 120 | 121 | (* fetch one integer from the code, and advance the program counter *) 122 | let fetch code pc = code.(pc), pc + 1 123 | 124 | let code_at_pc code pc = 125 | if 0 <= pc && pc < Array.length code 126 | then Printf.sprintf "code[%d..]=%d %d" pc code.(pc) 127 | ( if pc + 1 < Array.length code then code.(pc + 1) else -1 ) 128 | else Printf.sprintf "pc=%d" pc 129 | 130 | let dump_stack (sp, stack) = 131 | let rec loop i = if i = sp then "" else 132 | ( match stack.(i) with Int' i -> string_of_int i | Array' _ -> "array" | String' _ -> "string" ) 133 | ^ ";" ^ loop (i + 1) in "[" ^ loop 0 ^ "]" 134 | 135 | (* when the VM won't stop, you may turn on the following function to forcingly 136 | terminate after executing a certain amount of instructions *) 137 | let checkpoint = 138 | if false then (let counter = ref 5000 in fun () -> 139 | if !counter = 0 then failwith "expired!" else counter := !counter - 1) 140 | else fun () -> () 141 | 142 | let debug pc inst stack = with_debug (fun () -> Printf.printf "%d %s %s\n" (pc - 1) (show_inst inst) (dump_stack stack)) 143 | 144 | let rec interp code pc stack = 145 | checkpoint (); 146 | let open Value in 147 | if pc < 0 148 | then 149 | fst (pop stack) 150 | else try 151 | let i, pc = fetch code pc in 152 | let _ = Printf.printf "%d:%d\n" pc i in 153 | let inst = instsmap.(i) in 154 | debug pc inst stack; 155 | match inst with 156 | | UNIT -> 157 | interp code (pc + 1) stack 158 | | NOT -> 159 | let v, stack = pop stack in 160 | let stack = if int_of_value v = 0 then push stack (Int' 1) else push stack (Int' 0) in 161 | interp code pc stack 162 | | NEG -> 163 | let v, stack = pop stack in 164 | let stack = push stack (v |*| Int' (-1)) in 165 | interp code pc stack 166 | | ADD -> 167 | let v2, stack = pop stack in 168 | let v1, stack = pop stack in 169 | let stack = push stack (v1 |+| v2) in 170 | interp code pc stack 171 | | SUB -> 172 | let v2, stack = pop stack in 173 | let v1, stack = pop stack in 174 | let stack = push stack (v1 |-| v2) in 175 | interp code pc stack 176 | | MUL -> 177 | let v2, stack = pop stack in 178 | let v1, stack = pop stack in 179 | let stack = push stack (v1 |*| v2) in 180 | interp code pc stack 181 | | MOD -> 182 | let v2, stack = pop stack in 183 | let v1, stack = pop stack in 184 | let stack = push stack (v1 |%| v2) in 185 | interp code pc stack 186 | | DIV -> 187 | let v2, stack = pop stack in 188 | let v1, stack = pop stack in 189 | let stack = push stack (v1 |/| v2) in 190 | interp code pc stack 191 | | LT -> 192 | let v2, stack = pop stack in 193 | let v1, stack = pop stack in 194 | let stack = push stack (if v1 |<| v2 then Int' 1 else Int' 0) in 195 | interp code pc stack 196 | | EQ -> 197 | let v2, stack = pop stack in 198 | let v1, stack = pop stack in 199 | let stack = push stack (if v1 |=| v2 then Int' 1 else Int' 0) in 200 | interp code pc stack 201 | | CONST -> 202 | let c, pc = fetch code pc in 203 | let stack = push stack (value_of_int c) in 204 | interp code pc stack 205 | | CONST0 -> 206 | let stack = push stack (value_of_int 0) in 207 | interp code pc stack 208 | | JUMP_IF_ZERO (* addr *) -> 209 | let addr, pc = fetch code pc in 210 | let v, stack = pop stack in 211 | (* interp code (if v=0 then addr else pc) stack *) 212 | if int_of_value v = 0 213 | then interp code addr stack 214 | else interp code pc stack 215 | | CALL (* addr argnum *) -> 216 | (* calling a function will create a new operand stack and lvars *) 217 | let addr, pc = fetch code pc in 218 | let _, pc = fetch code pc in 219 | (match !stack_mode_flg with 220 | | `User_stack -> 221 | let stack = Emit.(if !sh_flg then push stack (Int' 200) else stack) in 222 | let stack = push stack (value_of_int pc) in 223 | (* save return address *) 224 | (* (let (sp,s)=stack in 225 | * if 2 232 | let stack = Emit.(if !sh_flg then push stack (Int' 100) else stack) in 233 | let v = interp code addr stack in 234 | let stack = push stack v in 235 | interp code pc stack) 236 | | RET (* n *) -> 237 | (* let pc0 = pc-1 in *) 238 | let n, pc = fetch code pc in 239 | let v, stack = pop stack in 240 | let _, stack = Emit.(if !sh_flg then pop stack else Int' 0, stack) in 241 | (match !stack_mode_flg with 242 | | `User_stack -> 243 | (* return value *) 244 | let pc, stack = pop stack in 245 | (* return address *) 246 | let stack = drop stack n in 247 | (* delete arguments *) 248 | let stack = push stack v in 249 | (* restore return value *) 250 | (* Printf.printf "%d RET with %d to %d\n" pc0 v pc; *) 251 | interp code (int_of_value pc) stack 252 | | `Host_stack -> v) 253 | | DUP -> 254 | let n, pc = fetch code pc in 255 | let stack = push stack (take stack n) in 256 | interp code pc stack 257 | | DUP0 -> 258 | let stack = push stack (take stack 0) in 259 | interp code pc stack 260 | | HALT -> fst (pop stack) (* just return the top value *) 261 | | FRAME_RESET (* o l n *) -> 262 | let o, pc = fetch code pc in 263 | let l, pc = fetch code pc in 264 | let n, pc = fetch code pc in 265 | let _ = Printf.printf "Frame Reset size: %d %d %d\n", o, l, n in 266 | let _ = with_debug (fun _ -> eprintf "o: %d, l %d, n: %d\n" o l n) in 267 | let stack = frame_reset stack o l n in 268 | interp code pc stack 269 | | POP1 -> 270 | let v, stack = pop stack in 271 | let _, stack = pop stack in 272 | let stack = push stack v in 273 | interp code pc stack 274 | | JUMP (* addr *) -> 275 | let n, _ = fetch code pc in 276 | interp code n stack 277 | | ARRAY_MAKE -> 278 | let init, stack = pop stack in 279 | let size, stack = pop stack in 280 | let stack = push stack (value_of_array (Array.make (int_of_value size) init)) in 281 | Printf.printf "ARRAY_MAKE size: 1\n" ; interp code pc stack 282 | | GET -> 283 | let n, stack = pop stack in 284 | let arr, stack = pop stack in 285 | let _ = Printf.printf "GET n: 1\n" in 286 | let stack = push stack (array_of_value arr).(int_of_value n) in 287 | interp code pc stack 288 | | PUT -> 289 | let i, stack = pop stack in 290 | let arr, stack = pop stack in 291 | let n, stack = pop stack in 292 | (array_of_value arr).(int_of_value i) <- n; 293 | let stack = push stack arr in 294 | interp code pc stack 295 | | PRINT_NEWLINE -> 296 | print_newline (); 297 | interp code pc stack 298 | | RAND_INT -> 299 | let n, stack = pop stack in 300 | let v = Random.int (int_of_value n) in 301 | let stack = push stack (value_of_int v) in 302 | interp code pc stack 303 | | READ_INT -> 304 | let v = read_int () in 305 | let stack = push stack (value_of_int v) in 306 | interp code pc stack 307 | | JIT_SETUP -> interp code pc stack 308 | | READ_STRING -> 309 | let v = read_line () in 310 | let stack = push stack (value_of_string v) in 311 | interp code pc stack 312 | | PRINT_INT -> 313 | let n, stack = pop stack in 314 | print_int (int_of_value n); 315 | let stack = push stack n in 316 | interp code pc stack 317 | | PRINT_STRING -> 318 | let s, stack = pop stack in 319 | let v = string_of_value s in print_string v; 320 | let stack = v |> String.length |> value_of_int |> push stack in 321 | interp code pc stack 322 | | _ -> failwith (sprintf "un matched pattern: %s" (show_inst inst)) 323 | with | e -> raise e 324 | 325 | let run_bin : fundef_bin_t -> int = fun fundefs -> 326 | let open Value in 327 | let stack = push (make_stack ()) (value_of_int (-987)) in 328 | int_of_value @@ interp fundefs 0 stack 329 | 330 | let run_asm : fundef_asm_t -> int = fun fundefs -> run_bin (Array.map int_of_inst fundefs) 331 | -------------------------------------------------------------------------------- /src/x64/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name x64) 3 | (public_name joe.x64) 4 | (flags (-w -4-33-40-41)) 5 | (libraries str MinCaml) 6 | (foreign_stubs (language c) (names libmincaml)) 7 | (preprocess (pps ppx_deriving.show))) 8 | -------------------------------------------------------------------------------- /src/x64/emit.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/x64/emit.ml -------------------------------------------------------------------------------- /src/x64/emit.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | 3 | val f : out_channel -> Asm.prog -> unit 4 | -------------------------------------------------------------------------------- /src/x64/lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NOTE: コンパイル後のシンボルのプリフィックスに _ がつくのを避ける 5 | void min_caml_print_int(long n) asm("min_caml_print_int"); 6 | void min_caml_print_newline() asm("min_caml_print_newline"); 7 | long* min_caml_create_array(long number_of_element, long init_value) asm("min_caml_create_array"); 8 | double* min_caml_create_float_array(long number_of_element, double float_value) asm("min_caml_create_float_array"); 9 | long min_caml_truncate() asm("min_caml_truncate"); 10 | void min_caml_print_float(double d) asm("min_caml_print_float"); 11 | void min_caml_print_byte(long n) asm("min_caml_print_byte"); 12 | long min_caml_read_int() asm("min_caml_read_int"); 13 | double min_caml_read_float() asm("min_caml_read_float"); 14 | double min_caml_atan(double x) asm("min_caml_atan"); 15 | double min_caml_cos(double x) asm("min_caml_cos"); 16 | double min_caml_floor(double x) asm("min_caml_floor"); 17 | double min_caml_sin(double x) asm("min_caml_sin"); 18 | double min_caml_abs_float(double x) asm("min_caml_abs_float"); 19 | double min_caml_float_of_int(long n) asm("min_caml_float_of_int"); 20 | long min_caml_int_of_float(double d) asm("min_caml_int_of_float"); 21 | double min_caml_sqrt(double d) asm("min_caml_sqrt"); 22 | 23 | void min_caml_print_int(long n) { 24 | printf("%ld", n); 25 | } 26 | 27 | void min_caml_print_newline() { 28 | printf("\n"); 29 | } 30 | 31 | long* min_caml_create_array(long number_of_element, long init_value) { 32 | long *heap_ptr; 33 | 34 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 35 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 36 | 37 | // Array の先頭アドレスを取得 38 | long *array_ptr = heap_ptr; 39 | 40 | for (long i = 0l; i < number_of_element; i++) { 41 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 42 | *heap_ptr = init_value; 43 | heap_ptr += 1; 44 | } 45 | 46 | // ヒープの先頭アドレスを x27 に書き戻す 47 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 48 | 49 | return array_ptr; 50 | } 51 | 52 | double* min_caml_create_float_array(long number_of_element, double float_value) { 53 | double *heap_ptr; 54 | 55 | // x27 に格納されたヒープのアドレスを heap_ptr へ書き出す 56 | asm volatile ("mov %0, x27" : "=r"(heap_ptr)); 57 | 58 | // Array の先頭アドレスを取得 59 | double *array_ptr = heap_ptr; 60 | 61 | for (long i = 0l; i < number_of_element; i++) { 62 | // Array へ書き込んだ後、ヒープの先頭アドレスを8バイト進める 63 | *heap_ptr = float_value; 64 | heap_ptr += 1; 65 | } 66 | 67 | // ヒープの先頭アドレスを x27 に書き戻す 68 | asm volatile ("mov x27, %0" : : "r"(heap_ptr)); 69 | 70 | return array_ptr; 71 | } 72 | 73 | // truncate 74 | long min_caml_truncate(double d) { 75 | return (long)d; 76 | } 77 | 78 | void min_caml_print_float(double d) { 79 | printf("%lf", d); 80 | } 81 | 82 | void min_caml_print_byte(long n) { 83 | putchar(n); 84 | } 85 | 86 | long min_caml_read_int() { 87 | long l; 88 | // fscanf(fp, "%ld", &l); 89 | scanf("%ld", &l); 90 | return l; 91 | } 92 | 93 | double min_caml_read_float() { 94 | double d; 95 | // fscanf(fp, "%lf", &d); 96 | scanf("%lf", &d); 97 | return d; 98 | } 99 | 100 | // atan 101 | double min_caml_atan(double x) { 102 | return atan(x); 103 | } 104 | 105 | // cos 106 | double min_caml_cos(double x) { 107 | return cos(x); 108 | } 109 | 110 | // floor 111 | double min_caml_floor(double x) { 112 | return floor(x); 113 | } 114 | 115 | // sin 116 | double min_caml_sin(double x) { 117 | return sin(x); 118 | } 119 | 120 | // abs_float 121 | double min_caml_abs_float(double x) { 122 | return fabs(x); 123 | } 124 | 125 | // float_of_int 126 | double min_caml_float_of_int(long n) { 127 | return (double)n; 128 | } 129 | 130 | // int_of_float 131 | long min_caml_int_of_float(double d) { 132 | return (long)d; 133 | } 134 | 135 | // sqrt 136 | double min_caml_sqrt(double d) { 137 | return sqrt(d); 138 | } 139 | -------------------------------------------------------------------------------- /src/x64/libmincaml.S: -------------------------------------------------------------------------------- 1 | #if defined(__CYGWIN__) || defined(__MACH__) 2 | #define U(x) _##x 3 | #else 4 | #define U(x) x 5 | #endif 6 | #if defined(__MACH__) 7 | #define ALIGNSTACK0 andl $0xfffffff0, %esp 8 | #define ALIGNSTACK1 andl $0xfffffff0, %esp; pushl %eax; pushl %eax; pushl %eax 9 | #define ALIGNSTACK2 andl $0xfffffff0, %esp; pushl %eax; pushl %eax 10 | #define ALIGNSTACK3 andl $0xfffffff0, %esp; pushl %eax 11 | #else 12 | #define ALIGNSTACK0 13 | #define ALIGNSTACK1 14 | #define ALIGNSTACK2 15 | #define ALIGNSTACK3 16 | #endif 17 | .text 18 | .globl min_caml_print_newline 19 | min_caml_print_newline: 20 | pushl %ebp 21 | movl %esp, %ebp 22 | ALIGNSTACK1 23 | pushl $10 24 | call U(putchar) 25 | movl %ebp, %esp 26 | popl %ebp 27 | ret 28 | .globl min_caml_print_int 29 | min_caml_print_int: 30 | pushl %ebp 31 | movl %esp, %ebp 32 | ALIGNSTACK2 33 | pushl %eax 34 | pushl $format_int 35 | call U(printf) 36 | movl %ebp, %esp 37 | popl %ebp 38 | ret 39 | .globl min_caml_print_byte 40 | min_caml_print_byte: 41 | pushl %ebp 42 | movl %esp, %ebp 43 | ALIGNSTACK1 44 | pushl %eax 45 | call U(putchar) 46 | movl %ebp, %esp 47 | popl %ebp 48 | ret 49 | .globl min_caml_prerr_int 50 | min_caml_prerr_int: 51 | pushl %ebp 52 | movl %esp, %ebp 53 | ALIGNSTACK3 54 | pushl %eax 55 | pushl $format_int 56 | pushl U(min_caml_stderr) 57 | call U(fprintf) 58 | movl %ebp, %esp 59 | popl %ebp 60 | ret 61 | .globl min_caml_prerr_byte 62 | min_caml_prerr_byte: 63 | pushl %ebp 64 | movl %esp, %ebp 65 | ALIGNSTACK2 66 | pushl U(min_caml_stderr) 67 | pushl %eax 68 | call U(fputc) 69 | movl %ebp, %esp 70 | popl %ebp 71 | ret 72 | .globl min_caml_prerr_float 73 | min_caml_prerr_float: 74 | pushl %ebp 75 | movl %esp, %ebp 76 | ALIGNSTACK0 77 | subl $8, %esp 78 | movsd %xmm0, (%esp) 79 | pushl $format_float 80 | pushl U(min_caml_stderr) 81 | call U(fprintf) 82 | movl %ebp, %esp 83 | popl %ebp 84 | ret 85 | .globl min_caml_read_int 86 | min_caml_read_int: 87 | pushl %ebp 88 | movl %esp, %ebp 89 | ALIGNSTACK3 90 | subl $4, %esp 91 | leal -4(%ebp), %eax 92 | pushl %eax 93 | pushl $format_int 94 | call U(scanf) 95 | movl -4(%ebp), %eax 96 | movl %ebp, %esp 97 | popl %ebp 98 | ret 99 | .globl min_caml_read_float 100 | min_caml_read_float: 101 | pushl %ebp 102 | movl %esp, %ebp 103 | ALIGNSTACK0 104 | subl $8, %esp 105 | leal -8(%ebp), %eax 106 | pushl %eax 107 | pushl $format_float 108 | call U(scanf) 109 | movsd -8(%ebp), %xmm0 110 | movl %ebp, %esp 111 | popl %ebp 112 | ret 113 | .globl min_caml_create_array 114 | min_caml_create_array: 115 | pushl %ebp 116 | movl %esp, %ebp 117 | pushl %ecx 118 | pushl %edx 119 | movl %eax, %ecx 120 | movl min_caml_hp, %eax 121 | movl %ecx, %edx 122 | shll $2, %edx 123 | addl %edx, min_caml_hp 124 | create_array_loop: 125 | cmpl $0, %ecx 126 | jne create_array_cont 127 | create_array_exit: 128 | popl %edx 129 | popl %ecx 130 | movl %ebp, %esp 131 | popl %ebp 132 | ret 133 | create_array_cont: 134 | decl %ecx 135 | movl %ebx, (%eax,%ecx,4) 136 | jmp create_array_loop 137 | .globl min_caml_create_float_array 138 | min_caml_create_float_array: 139 | pushl %ebp 140 | movl %esp, %ebp 141 | pushl %ecx 142 | pushl %edx 143 | movl %eax, %ecx 144 | movl min_caml_hp, %eax 145 | movl %ecx, %edx 146 | shll $3, %edx 147 | addl %edx, min_caml_hp 148 | create_float_array_loop: 149 | cmpl $0, %ecx 150 | jne create_float_array_cont 151 | create_float_array_exit: 152 | popl %edx 153 | popl %ecx 154 | movl %ebp, %esp 155 | popl %ebp 156 | ret 157 | create_float_array_cont: 158 | decl %ecx 159 | movsd %xmm0, (%eax,%ecx,8) 160 | jmp create_float_array_loop 161 | .globl min_caml_abs_float 162 | min_caml_abs_float: 163 | pushl %ebp 164 | movl %esp, %ebp 165 | ALIGNSTACK2 166 | subl $8, %esp 167 | movsd %xmm0, (%esp) 168 | call U(fabs) 169 | fstpl (%esp) 170 | movsd (%esp), %xmm0 171 | movl %ebp, %esp 172 | popl %ebp 173 | ret 174 | .globl min_caml_sqrt 175 | min_caml_sqrt: 176 | sqrtsd %xmm0, %xmm0 177 | ret 178 | .globl min_caml_floor 179 | min_caml_floor: 180 | pushl %ebp 181 | movl %esp, %ebp 182 | ALIGNSTACK2 183 | subl $8, %esp 184 | movsd %xmm0, (%esp) 185 | call U(floor) 186 | fstpl (%esp) 187 | movsd (%esp), %xmm0 188 | movl %ebp, %esp 189 | popl %ebp 190 | ret 191 | .globl min_caml_int_of_float 192 | min_caml_int_of_float: 193 | .globl min_caml_truncate 194 | min_caml_truncate: 195 | cvttsd2si %xmm0, %eax 196 | ret 197 | .globl min_caml_float_of_int 198 | min_caml_float_of_int: 199 | cvtsi2sd %eax, %xmm0 200 | ret 201 | .globl min_caml_cos 202 | min_caml_cos: 203 | pushl %ebp 204 | movl %esp, %ebp 205 | ALIGNSTACK2 206 | subl $8, %esp 207 | movsd %xmm0, (%esp) 208 | call U(cos) 209 | fstpl (%esp) 210 | movsd (%esp), %xmm0 211 | movl %ebp, %esp 212 | popl %ebp 213 | ret 214 | .globl min_caml_sin 215 | min_caml_sin: 216 | pushl %ebp 217 | movl %esp, %ebp 218 | ALIGNSTACK2 219 | subl $8, %esp 220 | movsd %xmm0, (%esp) 221 | call U(sin) 222 | fstpl (%esp) 223 | movsd (%esp), %xmm0 224 | movl %ebp, %esp 225 | popl %ebp 226 | ret 227 | .globl min_caml_atan 228 | min_caml_atan: 229 | pushl %ebp 230 | movl %esp, %ebp 231 | ALIGNSTACK2 232 | subl $8, %esp 233 | movsd %xmm0, (%esp) 234 | call U(atan) 235 | fstpl (%esp) 236 | movsd (%esp), %xmm0 237 | movl %ebp, %esp 238 | popl %ebp 239 | ret 240 | .globl min_caml_print_debug 241 | min_caml_print_debug: 242 | pushl %ebp 243 | movl %esp, %ebp 244 | ALIGNSTACK3 245 | pushl %ecx 246 | pushl %ebx 247 | pushl %eax 248 | call interp_debug 249 | movl %ebp, %esp 250 | popl %ebp 251 | ret 252 | .globl min_caml_rand_int 253 | min_caml_rand_int: 254 | pushl %ebp 255 | movl %esp, %ebp 256 | ALIGNSTACK1 257 | pushl %eax 258 | call rand_int 259 | movl %ebp, %esp 260 | popl %ebp 261 | ret 262 | .globl min_caml_modulo 263 | min_caml_modulo: 264 | pushl %ebp 265 | movl %esp, %ebp 266 | ALIGNSTACK2 267 | pushl %ebx 268 | pushl %eax 269 | call modulo 270 | movl %ebp, %esp 271 | popl %ebp 272 | ret 273 | .globl min_caml_print_string 274 | min_caml_print_string: 275 | ret 276 | .globl min_caml_read_string 277 | min_caml_read_string: 278 | ret 279 | .globl min_caml_divide 280 | min_caml_divide: 281 | pushl %ebp 282 | movl %esp, %ebp 283 | ALIGNSTACK2 284 | pushl %ebx 285 | pushl %eax 286 | call divide 287 | movl %ebp, %esp 288 | popl %ebp 289 | ret 290 | .data 291 | format_int: 292 | .asciz "%d" 293 | format_float: 294 | .asciz "%lf" 295 | .balign 8 296 | float_0: 297 | .long 0x0 298 | .long 0x0 299 | float_1: 300 | .long 0x0 301 | .long 0x3ff00000 302 | .balign 16 303 | .globl min_caml_fnegd 304 | min_caml_fnegd: 305 | .long 0 306 | .long 0x80000000 307 | .long 0 308 | .long 0 309 | .globl min_caml_hp 310 | min_caml_hp: 311 | .long 0x0 312 | -------------------------------------------------------------------------------- /src/x64/libmincaml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern int get_current_micros(void) asm ("min_caml_get_current_micros"); 8 | extern int modulo(int, int); 9 | extern int divide(int, int); 10 | extern int rand_int(int); 11 | 12 | int get_current_micros() { 13 | struct timeval current_time; 14 | gettimeofday(¤t_time, NULL); 15 | return current_time.tv_sec * (int)1e6 + current_time.tv_usec; 16 | } 17 | 18 | int modulo(int lhs, int rhs) { return lhs % rhs; } 19 | 20 | int divide(int lhs, int rhs) { return lhs / rhs; } 21 | 22 | int rand_int(int n) { 23 | srandom(get_current_micros()); 24 | return random() % n + 1; 25 | } 26 | -------------------------------------------------------------------------------- /src/x64/regAlloc.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/x64/regAlloc.ml -------------------------------------------------------------------------------- /src/x64/regAlloc.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | 3 | val f : Asm.prog -> Asm.prog 4 | -------------------------------------------------------------------------------- /src/x64/stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void min_caml_start(char *, char *); 5 | 6 | /* "stderr" is a macro and cannot be referred to in libmincaml.S, so 7 | this "min_caml_stderr" is used (in place of "__iob+32") for better 8 | portability (under SPARC emulators, for example). Thanks to Steven 9 | Shaw for reporting the problem and proposing this solution. */ 10 | FILE *min_caml_stderr; 11 | 12 | int main() { 13 | char *hp, *sp; 14 | 15 | min_caml_stderr = stderr; 16 | sp = alloca(1000000); hp = malloc(4000000); 17 | if (hp == NULL || sp == NULL) { 18 | fprintf(stderr, "malloc or alloca failed\n"); 19 | return 1; 20 | } 21 | // fprintf(stderr, "sp = %p, hp = %p\n", sp, hp); 22 | min_caml_start(sp, hp); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/x86/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name x86) 3 | (public_name joe.x86) 4 | (flags (-w -4-33-40-41)) 5 | (libraries str MinCaml) 6 | (foreign_stubs (language c) (names libmincaml)) 7 | (preprocess (pps ppx_deriving.show))) 8 | -------------------------------------------------------------------------------- /src/x86/emit.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/src/x86/emit.ml -------------------------------------------------------------------------------- /src/x86/emit.mli: -------------------------------------------------------------------------------- 1 | open MinCaml 2 | 3 | val f : out_channel -> Asm.prog -> unit 4 | -------------------------------------------------------------------------------- /src/x86/libmincaml.S: -------------------------------------------------------------------------------- 1 | #if defined(__CYGWIN__) || defined(__MACH__) 2 | #define U(x) _##x 3 | #else 4 | #define U(x) x 5 | #endif 6 | #if defined(__MACH__) 7 | #define ALIGNSTACK0 andl $0xfffffff0, %esp 8 | #define ALIGNSTACK1 andl $0xfffffff0, %esp; pushl %eax; pushl %eax; pushl %eax 9 | #define ALIGNSTACK2 andl $0xfffffff0, %esp; pushl %eax; pushl %eax 10 | #define ALIGNSTACK3 andl $0xfffffff0, %esp; pushl %eax 11 | #else 12 | #define ALIGNSTACK0 13 | #define ALIGNSTACK1 14 | #define ALIGNSTACK2 15 | #define ALIGNSTACK3 16 | #endif 17 | .text 18 | .globl min_caml_print_newline 19 | min_caml_print_newline: 20 | pushl %ebp 21 | movl %esp, %ebp 22 | ALIGNSTACK1 23 | pushl $10 24 | call U(putchar) 25 | movl %ebp, %esp 26 | popl %ebp 27 | ret 28 | .globl min_caml_print_int 29 | min_caml_print_int: 30 | pushl %ebp 31 | movl %esp, %ebp 32 | ALIGNSTACK2 33 | pushl %eax 34 | pushl $format_int 35 | call U(printf) 36 | movl %ebp, %esp 37 | popl %ebp 38 | ret 39 | .globl min_caml_print_byte 40 | min_caml_print_byte: 41 | pushl %ebp 42 | movl %esp, %ebp 43 | ALIGNSTACK1 44 | pushl %eax 45 | call U(putchar) 46 | movl %ebp, %esp 47 | popl %ebp 48 | ret 49 | .globl min_caml_prerr_int 50 | min_caml_prerr_int: 51 | pushl %ebp 52 | movl %esp, %ebp 53 | ALIGNSTACK3 54 | pushl %eax 55 | pushl $format_int 56 | pushl U(min_caml_stderr) 57 | call U(fprintf) 58 | movl %ebp, %esp 59 | popl %ebp 60 | ret 61 | .globl min_caml_prerr_byte 62 | min_caml_prerr_byte: 63 | pushl %ebp 64 | movl %esp, %ebp 65 | ALIGNSTACK2 66 | pushl U(min_caml_stderr) 67 | pushl %eax 68 | call U(fputc) 69 | movl %ebp, %esp 70 | popl %ebp 71 | ret 72 | .globl min_caml_prerr_float 73 | min_caml_prerr_float: 74 | pushl %ebp 75 | movl %esp, %ebp 76 | ALIGNSTACK0 77 | subl $8, %esp 78 | movsd %xmm0, (%esp) 79 | pushl $format_float 80 | pushl U(min_caml_stderr) 81 | call U(fprintf) 82 | movl %ebp, %esp 83 | popl %ebp 84 | ret 85 | .globl min_caml_read_int 86 | min_caml_read_int: 87 | pushl %ebp 88 | movl %esp, %ebp 89 | ALIGNSTACK3 90 | subl $4, %esp 91 | leal -4(%ebp), %eax 92 | pushl %eax 93 | pushl $format_int 94 | call U(scanf) 95 | movl -4(%ebp), %eax 96 | movl %ebp, %esp 97 | popl %ebp 98 | ret 99 | .globl min_caml_read_float 100 | min_caml_read_float: 101 | pushl %ebp 102 | movl %esp, %ebp 103 | ALIGNSTACK0 104 | subl $8, %esp 105 | leal -8(%ebp), %eax 106 | pushl %eax 107 | pushl $format_float 108 | call U(scanf) 109 | movsd -8(%ebp), %xmm0 110 | movl %ebp, %esp 111 | popl %ebp 112 | ret 113 | .globl min_caml_create_array 114 | min_caml_create_array: 115 | pushl %ebp 116 | movl %esp, %ebp 117 | pushl %ecx 118 | pushl %edx 119 | movl %eax, %ecx 120 | movl min_caml_hp, %eax 121 | movl %ecx, %edx 122 | shll $2, %edx 123 | addl %edx, min_caml_hp 124 | create_array_loop: 125 | cmpl $0, %ecx 126 | jne create_array_cont 127 | create_array_exit: 128 | popl %edx 129 | popl %ecx 130 | movl %ebp, %esp 131 | popl %ebp 132 | ret 133 | create_array_cont: 134 | decl %ecx 135 | movl %ebx, (%eax,%ecx,4) 136 | jmp create_array_loop 137 | .globl min_caml_create_float_array 138 | min_caml_create_float_array: 139 | pushl %ebp 140 | movl %esp, %ebp 141 | pushl %ecx 142 | pushl %edx 143 | movl %eax, %ecx 144 | movl min_caml_hp, %eax 145 | movl %ecx, %edx 146 | shll $3, %edx 147 | addl %edx, min_caml_hp 148 | create_float_array_loop: 149 | cmpl $0, %ecx 150 | jne create_float_array_cont 151 | create_float_array_exit: 152 | popl %edx 153 | popl %ecx 154 | movl %ebp, %esp 155 | popl %ebp 156 | ret 157 | create_float_array_cont: 158 | decl %ecx 159 | movsd %xmm0, (%eax,%ecx,8) 160 | jmp create_float_array_loop 161 | .globl min_caml_abs_float 162 | min_caml_abs_float: 163 | pushl %ebp 164 | movl %esp, %ebp 165 | ALIGNSTACK2 166 | subl $8, %esp 167 | movsd %xmm0, (%esp) 168 | call U(fabs) 169 | fstpl (%esp) 170 | movsd (%esp), %xmm0 171 | movl %ebp, %esp 172 | popl %ebp 173 | ret 174 | .globl min_caml_sqrt 175 | min_caml_sqrt: 176 | sqrtsd %xmm0, %xmm0 177 | ret 178 | .globl min_caml_floor 179 | min_caml_floor: 180 | pushl %ebp 181 | movl %esp, %ebp 182 | ALIGNSTACK2 183 | subl $8, %esp 184 | movsd %xmm0, (%esp) 185 | call U(floor) 186 | fstpl (%esp) 187 | movsd (%esp), %xmm0 188 | movl %ebp, %esp 189 | popl %ebp 190 | ret 191 | .globl min_caml_int_of_float 192 | min_caml_int_of_float: 193 | .globl min_caml_truncate 194 | min_caml_truncate: 195 | cvttsd2si %xmm0, %eax 196 | ret 197 | .globl min_caml_float_of_int 198 | min_caml_float_of_int: 199 | cvtsi2sd %eax, %xmm0 200 | ret 201 | .globl min_caml_cos 202 | min_caml_cos: 203 | pushl %ebp 204 | movl %esp, %ebp 205 | ALIGNSTACK2 206 | subl $8, %esp 207 | movsd %xmm0, (%esp) 208 | call U(cos) 209 | fstpl (%esp) 210 | movsd (%esp), %xmm0 211 | movl %ebp, %esp 212 | popl %ebp 213 | ret 214 | .globl min_caml_sin 215 | min_caml_sin: 216 | pushl %ebp 217 | movl %esp, %ebp 218 | ALIGNSTACK2 219 | subl $8, %esp 220 | movsd %xmm0, (%esp) 221 | call U(sin) 222 | fstpl (%esp) 223 | movsd (%esp), %xmm0 224 | movl %ebp, %esp 225 | popl %ebp 226 | ret 227 | .globl min_caml_atan 228 | min_caml_atan: 229 | pushl %ebp 230 | movl %esp, %ebp 231 | ALIGNSTACK2 232 | subl $8, %esp 233 | movsd %xmm0, (%esp) 234 | call U(atan) 235 | fstpl (%esp) 236 | movsd (%esp), %xmm0 237 | movl %ebp, %esp 238 | popl %ebp 239 | ret 240 | .globl min_caml_print_debug 241 | min_caml_print_debug: 242 | pushl %ebp 243 | movl %esp, %ebp 244 | ALIGNSTACK3 245 | pushl %ecx 246 | pushl %ebx 247 | pushl %eax 248 | call interp_debug 249 | movl %ebp, %esp 250 | popl %ebp 251 | ret 252 | .globl min_caml_rand_int 253 | min_caml_rand_int: 254 | pushl %ebp 255 | movl %esp, %ebp 256 | ALIGNSTACK1 257 | pushl %eax 258 | call rand_int 259 | movl %ebp, %esp 260 | popl %ebp 261 | ret 262 | .globl min_caml_modulo 263 | min_caml_modulo: 264 | pushl %ebp 265 | movl %esp, %ebp 266 | ALIGNSTACK2 267 | pushl %ebx 268 | pushl %eax 269 | call modulo 270 | movl %ebp, %esp 271 | popl %ebp 272 | ret 273 | .globl min_caml_divide 274 | min_caml_divide: 275 | pushl %ebp 276 | movl %esp, %ebp 277 | ALIGNSTACK2 278 | pushl %ebx 279 | pushl %eax 280 | call divide 281 | movl %ebp, %esp 282 | popl %ebp 283 | ret 284 | .data 285 | format_int: 286 | .asciz "%d" 287 | format_float: 288 | .asciz "%lf" 289 | .balign 8 290 | float_0: 291 | .long 0x0 292 | .long 0x0 293 | float_1: 294 | .long 0x0 295 | .long 0x3ff00000 296 | .balign 16 297 | .globl min_caml_fnegd 298 | min_caml_fnegd: 299 | .long 0 300 | .long 0x80000000 301 | .long 0 302 | .long 0 303 | .globl min_caml_hp 304 | min_caml_hp: 305 | .long 0x0 306 | -------------------------------------------------------------------------------- /src/x86/libmincaml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern int get_current_micros(void) asm ("min_caml_get_current_micros"); 8 | extern int modulo(int, int); 9 | extern int divide(int, int); 10 | extern int rand_int(int); 11 | 12 | int get_current_micros() { 13 | struct timeval current_time; 14 | gettimeofday(¤t_time, NULL); 15 | return current_time.tv_sec * (int)1e6 + current_time.tv_usec; 16 | } 17 | 18 | int modulo(int lhs, int rhs) { return lhs % rhs; } 19 | 20 | int divide(int lhs, int rhs) { return lhs / rhs; } 21 | 22 | int rand_int(int n) { 23 | srandom(get_current_micros()); 24 | return random() % n + 1; 25 | } 26 | -------------------------------------------------------------------------------- /styles/_aside.scss: -------------------------------------------------------------------------------- 1 | aside { 2 | max-width: 650px; 3 | margin: auto; 4 | padding: 8px; 5 | font-size: 20px; 6 | text-align: right; 7 | time, div { 8 | margin-bottom: 20px; 9 | display: block; 10 | } 11 | } -------------------------------------------------------------------------------- /styles/_canvas.scss: -------------------------------------------------------------------------------- 1 | canvas { 2 | max-width: 100%; 3 | position: absolute; 4 | z-index: -10; 5 | } -------------------------------------------------------------------------------- /styles/_code.scss: -------------------------------------------------------------------------------- 1 | code { 2 | display: inline-block; 3 | overflow-x: auto; 4 | max-width: 100%; 5 | padding: 12px; 6 | margin: 8px 0; 7 | white-space: pre; 8 | text-align: left; 9 | border-radius: 4px; 10 | font-size: 14px; 11 | color: #00259c; 12 | background: white; 13 | box-shadow: 0 1px 4px #f1f0f0, inset 0 0 0 1px #e7e6e5; 14 | @media (min-width: 768px) { 15 | font-size: 17px; 16 | } 17 | } 18 | 19 | pre { 20 | white-space: normal; 21 | max-width: 100%; 22 | width: 650px; 23 | padding: 12px; 24 | margin: 8px auto; 25 | text-align: left; 26 | border-radius: 4px; 27 | font-size: 14px; 28 | color: #00259c; 29 | background: white; 30 | box-shadow: 0 1px 4px #f1f0f0, inset 0 0 0 1px #e7e6e5; 31 | @media (min-width: 768px) { 32 | font-size: 17px; 33 | } 34 | } 35 | 36 | .h__name { 37 | color: #00259c; 38 | font-weight: bold; 39 | } 40 | 41 | .h__keyword { 42 | color: #ca30d4; 43 | } 44 | 45 | .h__symbol { 46 | color: #9f9fa3; 47 | } -------------------------------------------------------------------------------- /styles/_exe.scss: -------------------------------------------------------------------------------- 1 | .exe { 2 | 3 | figure { 4 | max-width: inherit; 5 | } 6 | 7 | h1 { 8 | color: #7D8A96; 9 | font-size: 32px; 10 | text-transform: uppercase; 11 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 12 | display: inline-block; 13 | margin: 42px 0 10px; 14 | @media(min-width: 768px) { 15 | font-size: 40px; 16 | } 17 | } 18 | 19 | h1 + h2 { 20 | margin-top: 0px; 21 | } 22 | } 23 | 24 | .macro { 25 | margin: 0 auto; 26 | display: flex; 27 | justify-content: space-around; 28 | max-width: 600px; 29 | flex-wrap: wrap; 30 | width: 100%; 31 | &__col { 32 | h3 { 33 | padding: 0; 34 | width: initial; 35 | } 36 | padding-left: 24px; 37 | line-height: 1.5; 38 | font-size: 14px; 39 | @media(min-width: 768px) { 40 | font-size: 22px; 41 | } 42 | } 43 | } 44 | 45 | .langf { 46 | @media(min-width: 920px) { 47 | display: flex; 48 | flex-direction: row; 49 | justify-content: center; 50 | } 51 | 52 | ol { 53 | display: inline-block; 54 | max-width: 650px; 55 | margin: auto; 56 | font-size: 20px; 57 | padding-left: 36px; 58 | } 59 | 60 | &__col { 61 | @media(min-width: 920px) { 62 | .langf-col:last-child { 63 | white-space: pre; 64 | margin-left: 20px; 65 | } 66 | 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /styles/_footer.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | width: 100%; 3 | padding: 15px; 4 | background: #7F8C8D; 5 | color: white; 6 | text-align: center; 7 | 8 | &__logo { 9 | width: 50px; 10 | margin: 20px; 11 | } 12 | 13 | &__copy { 14 | font-size: 16px; 15 | white-space: pre; 16 | } 17 | @media(min-width: 768px) { 18 | &__copy { 19 | font-size: 24px; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /styles/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | background: black; 3 | color: white; 4 | position: relative; 5 | min-height: 450px; 6 | padding: 16px 8px; 7 | display: flex; 8 | text-align: center; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | width: 100%; 13 | z-index: 5; 14 | 15 | &__logo { 16 | width: 130px; 17 | position: relative; 18 | } 19 | 20 | &__titles { 21 | position: relative; 22 | max-width: 800px; 23 | margin: 20px; 24 | text-shadow: 1px 1px 7px #56CCF2; 25 | } 26 | 27 | &__title { 28 | font-size: 48px; 29 | line-height: 1.1; 30 | } 31 | 32 | &__subtitle { 33 | font-size: 22px; 34 | } 35 | 36 | @media(min-width: 768px) { 37 | flex-direction: row; 38 | 39 | &__logo { 40 | width: 160px; 41 | margin-right: 10px; 42 | } 43 | 44 | &__title { 45 | font-size: 60px; 46 | } 47 | 48 | &__subtitle { 49 | font-size: 30px; 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /styles/_index.scss: -------------------------------------------------------------------------------- 1 | .index { 2 | max-width: 900px; 3 | margin: auto; 4 | padding: 20px 0; 5 | display: flex; 6 | flex-wrap: wrap; 7 | justify-content: center; 8 | 9 | &__col { 10 | white-space: nowrap; 11 | padding: 15px 25px; 12 | margin: 15px; 13 | background: #fefefe; 14 | box-shadow: 0 1px 4px rgba(0, 0, 100, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 15 | transition: box-shadow 0.5s cubic-bezier(0.23, 1, 0.32, 1); 16 | 17 | &:hover { 18 | box-shadow: 0 8px 40px rgba(0, 0, 100, 0.15), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 19 | } 20 | } 21 | 22 | a { 23 | line-height: 1.5; 24 | color: #888; 25 | font-size: 24px; 26 | } 27 | 28 | a[href^="#"] { 29 | color: #BBBBBB; 30 | } 31 | 32 | a:hover { 33 | color: #3A98C8; 34 | } 35 | 36 | a[href^="#"]:hover { 37 | color: #BBBBBB; 38 | } 39 | 40 | h2 { 41 | font-size: 24px; 42 | text-align: left; 43 | color: #707070; 44 | margin: 0 0 10px; 45 | text-transform: uppercase; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /styles/_intro.scss: -------------------------------------------------------------------------------- 1 | .intro { 2 | width: 650px; 3 | max-width: 100%; 4 | margin: auto; 5 | } -------------------------------------------------------------------------------- /styles/_list.scss: -------------------------------------------------------------------------------- 1 | .list { 2 | h1 { 3 | color: #7D8A96; 4 | font-size: 32px; 5 | text-transform: uppercase; 6 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 7 | display: inline-block; 8 | margin: 42px 8px 10px; 9 | @media(min-width: 768px) { 10 | font-size: 40px; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /styles/_om.scss: -------------------------------------------------------------------------------- 1 | .om { 2 | 3 | figure { 4 | max-width: inherit; 5 | } 6 | 7 | section { 8 | h1 { 9 | margin-top: 50px; 10 | } 11 | } 12 | 13 | h1 { 14 | color: #7D8A96; 15 | font-size: 32px; 16 | text-transform: uppercase; 17 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 18 | display: inline-block; 19 | margin-bottom: 32px; 20 | @media(min-width: 768px) { 21 | font-size: 40px; 22 | } 23 | } 24 | 25 | h1 + h2 { 26 | margin-top: 20px; 27 | } 28 | 29 | h3 { 30 | max-width: 100%; 31 | margin: auto; 32 | width: 600px; 33 | padding: 16px 8px 0; 34 | } 35 | } -------------------------------------------------------------------------------- /styles/_resources.scss: -------------------------------------------------------------------------------- 1 | .resources { 2 | 3 | max-width: 650px; 4 | margin: 32px auto 0; 5 | text-align: left; 6 | padding: 8px; 7 | 8 | &__title { 9 | display: inline-block; 10 | margin-bottom: 10px; 11 | color: #7D8A96; 12 | font-size: 32px; 13 | text-transform: uppercase; 14 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 15 | @media(min-width: 768px) { 16 | font-size: 40px; 17 | } 18 | } 19 | &__list { 20 | padding-left: 32px; 21 | line-height: 1.5; 22 | font-size: 18px; 23 | @media(min-width: 768px) { 24 | font-size: 22px; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /styles/_semantics.scss: -------------------------------------------------------------------------------- 1 | .semantics { 2 | text-align: center; 3 | 4 | figure { 5 | display: inline-block; 6 | max-width: min-content; 7 | font-size: 13px; 8 | @media(min-width:800px) { 9 | font-size: 16px; 10 | } 11 | } 12 | 13 | h1 { 14 | color: #7D8A96; 15 | font-size: 32px; 16 | text-transform: uppercase; 17 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 18 | display: inline-block; 19 | margin-bottom: 8px; 20 | } 21 | 22 | section { 23 | margin-top: 40px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /styles/_stack.scss: -------------------------------------------------------------------------------- 1 | .stack { 2 | margin: auto; 3 | max-width: 100%; 4 | border-spacing: 10px; 5 | color: #282828; 6 | font-size: 20px; 7 | 8 | td { 9 | background-color:#fff9a6; 10 | // width: 80px; 11 | padding: 4px; 12 | outline: 1px solid rgba(0, 0, 0, .3); 13 | } 14 | 15 | th { 16 | padding: 4px; 17 | text-align: left; 18 | font-weight: normal; 19 | } 20 | 21 | .empty { 22 | background-color: inherit; 23 | outline: inherit; 24 | 25 | } 26 | 27 | } 28 | 29 | 30 | 31 | 32 | @media(max-width: 600px) { 33 | .stack { 34 | font-size: 16px; 35 | display: inline-block; 36 | border-spacing: 0; 37 | tbody { 38 | display: block; 39 | } 40 | 41 | tr { 42 | display: block; 43 | text-align: left; 44 | vertical-align: top; 45 | position: relative; 46 | } 47 | 48 | td { 49 | margin: 30px 0 10px; 50 | text-align: center; 51 | padding: 4px; 52 | display: inline-block; 53 | width: 90px; 54 | } 55 | 56 | td[colspan="4"] { 57 | width: 360px; 58 | } 59 | 60 | td[colspan="2"] { 61 | width: 180px; 62 | } 63 | 64 | th { 65 | position: absolute; 66 | top: 0; 67 | width: 100%; 68 | } 69 | } 70 | } 71 | 72 | 73 | 74 | @media(max-width: 320px) { 75 | .stack { 76 | font-size: 12px; 77 | td { 78 | margin: 20px 0 5px; 79 | width: 80px; 80 | } 81 | 82 | td[colspan="4"] { 83 | width: 320px; 84 | } 85 | 86 | td[colspan="2"] { 87 | width: 160px; 88 | } 89 | } 90 | } 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /styles/_status.scss: -------------------------------------------------------------------------------- 1 | .status { 2 | text-align: left; 3 | display: inline-block; 4 | padding-left: 32px; 5 | 6 | ol { 7 | line-height: 1.5; 8 | font-size: 18px; 9 | @media(min-width: 768px) { 10 | font-size: 22px; 11 | } 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /styles/_types.scss: -------------------------------------------------------------------------------- 1 | .types { 2 | display: inline-block; 3 | text-align: left; 4 | max-width: 100%; 5 | padding: 0 8px; 6 | h1 { 7 | color: #7D8A96; 8 | font-size: 32px; 9 | text-transform: uppercase; 10 | border-bottom: 1px solid rgba(0, 0 , 0,0.3); 11 | display: inline-block; 12 | margin: 42px 0 10px; 13 | @media(min-width: 768px) { 14 | font-size: 40px; 15 | } 16 | } 17 | 18 | section { 19 | max-width: 100%; 20 | } 21 | 22 | p { 23 | margin: 0; 24 | padding-left: 0; 25 | } 26 | 27 | 28 | 29 | h1 + h2 { 30 | margin-top: 0px; 31 | } 32 | 33 | 34 | .type { 35 | max-width: 660px; 36 | display: flex; 37 | flex-flow: row wrap; 38 | &__col { 39 | padding-left: 22px; 40 | flex: 1 0 15%; 41 | h3 { 42 | width: initial; 43 | padding: 0; 44 | margin: 0; 45 | } 46 | @media(min-width: 600px) { 47 | padding-right: 22px; 48 | flex: 1 0 15%; 49 | font-size: 20px; 50 | } 51 | } 52 | } 53 | 54 | .legend { 55 | margin: 20px auto 0; 56 | display: inline-block; 57 | padding: 10px 20px; 58 | font-size: 24px; 59 | background: #FDF6E3; 60 | border-radius: 4px; 61 | box-shadow: 0 4px 6px rgba(50,50,93,.11), 62 | 0 1px 3px rgba(0, 0, 0,.08); 63 | } 64 | } -------------------------------------------------------------------------------- /styles/main.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'local'; 3 | src: url('https://groupoid.space/Geometria-Light.otf'); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | 8 | .MathJax_Display { 9 | overflow-x: auto; 10 | overflow-y: hidden; 11 | } 12 | 13 | nav a { 14 | font-size: 18px; 15 | border: 2px solid #dedede; 16 | background-color: white; 17 | color: lightblue; 18 | text-decoration: none; 19 | margin: 5px 5px; 20 | padding: 7px 12px; 21 | min-width: 150px; 22 | text-align: center; 23 | } 24 | 25 | nav a:visited { 26 | color: lightblue; 27 | } 28 | 29 | nav a:hover { 30 | border-bottom: 2px solid #00b8cf; 31 | } 32 | 33 | 34 | nav { 35 | display: flex; 36 | background-color: #FBFBFB; 37 | flex-direction: row; 38 | justify-content: center; 39 | } 40 | 41 | nav a { 42 | font-size: 18px; 43 | min-width: initial; 44 | } 45 | 46 | 47 | * { 48 | margin: 0; 49 | padding: 0; 50 | box-sizing: border-box; 51 | } 52 | 53 | sub, 54 | sup { 55 | font-size: 80%; 56 | line-height: 0; 57 | position: relative; 58 | vertical-align: baseline; 59 | } 60 | 61 | sub { 62 | bottom: -0.25em; 63 | } 64 | 65 | sup { 66 | top: -0.5em; 67 | } 68 | 69 | html { 70 | height: 100%; 71 | } 72 | 73 | body { 74 | min-height: 100%; 75 | text-rendering: optimizeLegibility; 76 | -webkit-font-smoothing: antialiased; 77 | } 78 | 79 | h1, h2, h3, h4 { 80 | font-weight: normal; 81 | } 82 | 83 | ul { 84 | list-style-type: none; 85 | } 86 | 87 | ol, 88 | ul { 89 | text-align: left; 90 | line-height: 1.5; 91 | } 92 | 93 | img, figure { 94 | vertical-align: middle; 95 | } 96 | 97 | img { margin-left:20px; } 98 | 99 | .content { 100 | min-height: 100%; 101 | font-family: local; 102 | display: flex; // sticky footer 103 | flex-direction: column; 104 | position: relative; 105 | } 106 | 107 | .main { 108 | color: #586E75; 109 | background: #FBFBFB; 110 | padding: 50px 0px; 111 | text-align: center; 112 | box-shadow: 0 3px 10px rgba(0,0,0,0); 113 | // 0 -3px 10px rgba(0,0,0,0.3); 114 | z-index: 5; 115 | flex: 1 1 auto; // sticky footer 116 | 117 | } 118 | 119 | figure { 120 | min-width: 300px; 121 | overflow-x: auto; 122 | padding: 10px 2px; 123 | font-size: 18px; 124 | display: block; 125 | } 126 | 127 | figure::after { 128 | content: "\A"; 129 | white-space: pre; 130 | } 131 | 132 | h2 { 133 | font-size: 24px; 134 | color: #268BD2; 135 | @media(min-width: 768px) { 136 | font-size: 32px; 137 | } 138 | margin: 34px 0 8px; 139 | } 140 | 141 | h3 { 142 | max-width: 100%; 143 | margin: 16px auto 0; 144 | // width: 650px; 145 | padding: 0 0 0 8px; 146 | text-transform: uppercase; 147 | text-align: left; 148 | font-size: 22px; 149 | } 150 | 151 | p { 152 | max-width: 100%; 153 | width: 650px; 154 | margin: auto; 155 | padding: 8px; 156 | text-align: left; 157 | font-size: 18px; 158 | line-height: 1.4; 159 | @media(min-width: 768px) { 160 | font-size: 22px; 161 | } 162 | } 163 | 164 | p:hover { 165 | background: white; 166 | } 167 | 168 | mark { 169 | background: #FDF6E3; 170 | padding: 0px 2px; 171 | border-radius: 2px; 172 | color: #586E75; 173 | } 174 | 175 | a { 176 | color: #3A98C8; 177 | text-decoration: none; 178 | &:visited { 179 | color: #3A98C8; 180 | } 181 | 182 | &:hover { 183 | color: #D33682; 184 | } 185 | 186 | &:active { 187 | color: #D33682; 188 | } 189 | } 190 | 191 | 192 | @import '_aside'; 193 | @import '_header'; 194 | @import '_footer'; 195 | @import '_semantics'; 196 | @import '_intro'; 197 | @import '_status'; 198 | @import '_resources'; 199 | @import '_index'; 200 | @import '_om'; 201 | @import '_exe'; 202 | @import '_types'; 203 | @import '_list'; 204 | @import '_code'; 205 | @import '_canvas'; 206 | @import '_stack'; 207 | 208 | -------------------------------------------------------------------------------- /styles/mathjax.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var newMathJax = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js'; 3 | var oldMathJax = 'cdn.mathjax.org/mathjax/latest/MathJax.js'; 4 | var replaceScript = function (script, src) { 5 | var newScript = document.createElement('script'); 6 | newScript.src = newMathJax + src.replace(/.*?(\?|$)/, '$1'); 7 | newScript.onload = script.onload; 8 | newScript.onerror = script.onerror; 9 | script.onload = script.onerror = null; 10 | while (script.firstChild) newScript.appendChild(script.firstChild); 11 | if (script.id != null) newScript.id = script.id; 12 | script.parentNode.replaceChild(newScript, script); 13 | console.info('MathJax: 2.7.1.'); 14 | } 15 | 16 | if (document.currentScript) { 17 | var script = document.currentScript; 18 | replaceScript(script, script.src); 19 | } else { 20 | var n = oldMathJax.length; 21 | var scripts = document.getElementsByTagName('script'); 22 | for (var i = 0; i < scripts.length; i++) { 23 | var script = scripts[i]; 24 | var src = (script.src || '').replace(/.*?:\/\//,''); 25 | if (src.substr(0, n) === oldMathJax) { 26 | replaceScript(script, src); 27 | break; 28 | } 29 | } 30 | } 31 | })(); 32 | -------------------------------------------------------------------------------- /vm.opam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupoid/joe/4bbf1a33ef17e076df3bac47fe32e5604243b2d5/vm.opam --------------------------------------------------------------------------------