├── docs └── cfg │ ├── log1.input │ ├── log2.input │ ├── log5.input │ ├── log3.input │ ├── log4.input │ ├── for1.input │ ├── if1.input │ ├── while1.input │ ├── while2.input │ ├── for4.input │ ├── if2.input │ ├── for2.input │ ├── repeat1.input │ ├── for3.input │ ├── if3.input │ ├── if4-goto.input │ ├── log5.gv │ ├── repeat1.gv │ ├── log1.gv │ ├── log2.gv │ ├── while1.gv │ ├── if1.gv │ ├── while2.gv │ ├── if2.gv │ ├── log3.gv │ ├── log4.gv │ ├── gencfg.sh │ ├── for1.gv │ ├── log5.svg │ ├── for4.gv │ ├── if3.gv │ ├── if4-goto.gv │ ├── for2.gv │ ├── repeat1.svg │ ├── log2.svg │ ├── log1.svg │ ├── while1.svg │ ├── while2.svg │ ├── if1.svg │ ├── for3.gv │ ├── if2.svg │ ├── log3.svg │ └── log4.svg ├── src ├── chibicc │ ├── README.md │ ├── .clang-format │ └── chibicc_strings.c ├── cfg.h ├── README.md ├── common.h ├── optimizer.h ├── dominator.h ├── codegen.h ├── fnv_hash.h ├── dataflow_framework.h ├── membuf.h ├── fnv_hash.c ├── allocate.c ├── set.h ├── hash_table.h ├── cfg.c ├── opt_unusedcode.c ├── ravi_binding.c ├── dataflow_framework.c ├── df_liveness.c ├── graph.h ├── allocate.h └── bitset.h ├── examples ├── sieve_aot.sh ├── matrixmul_aot.sh ├── gaussian2_aot.sh ├── sieve_aot.lua ├── matrixmul_aot.lua ├── sieve_lib.lua ├── matrixmul_lib.lua ├── gaussian2_aot.lua ├── README.md └── gaussian2_lib.lua ├── .clang-format ├── .gitignore ├── tests ├── input │ ├── t07_concat.in │ ├── t02_locals.in │ ├── t01_globals.in │ ├── t06_close.in │ ├── t08_genericfor.in │ ├── t05_upvals.in │ ├── t04_stmts_1.in │ ├── t03_types.in │ ├── t00_exprs.in │ ├── t09_bugs.in │ └── t10_embed_C.in ├── expected │ └── tgraph.expected ├── tchibicc.c ├── tcommon.h ├── truntests.sh ├── README.md ├── tcommon.c └── tgraph.c ├── utils ├── tokenstr.h ├── tokenenum.h └── tokenmacro.h ├── .devcontainer ├── Dockerfile ├── base.Dockerfile └── devcontainer.json ├── .github └── workflows │ └── build.yml ├── include └── ravi_api.h ├── LICENSE ├── CMakeLists.txt └── README.md /docs/cfg/log1.input: -------------------------------------------------------------------------------- 1 | return a and b 2 | -------------------------------------------------------------------------------- /docs/cfg/log2.input: -------------------------------------------------------------------------------- 1 | return a or b 2 | -------------------------------------------------------------------------------- /docs/cfg/log5.input: -------------------------------------------------------------------------------- 1 | return not b 2 | -------------------------------------------------------------------------------- /docs/cfg/log3.input: -------------------------------------------------------------------------------- 1 | return a or b and c 2 | -------------------------------------------------------------------------------- /docs/cfg/log4.input: -------------------------------------------------------------------------------- 1 | return a and b or c 2 | -------------------------------------------------------------------------------- /docs/cfg/for1.input: -------------------------------------------------------------------------------- 1 | for i=1,10 do print(i) end 2 | -------------------------------------------------------------------------------- /docs/cfg/if1.input: -------------------------------------------------------------------------------- 1 | if 1 < 2 then print 'hi' end 2 | -------------------------------------------------------------------------------- /docs/cfg/while1.input: -------------------------------------------------------------------------------- 1 | while true do print('forever') end 2 | -------------------------------------------------------------------------------- /docs/cfg/while2.input: -------------------------------------------------------------------------------- 1 | local a while a < 10 do a = a + 1 end 2 | -------------------------------------------------------------------------------- /docs/cfg/for4.input: -------------------------------------------------------------------------------- 1 | for i =1,10 do if i == 2 then break end end 2 | -------------------------------------------------------------------------------- /docs/cfg/if2.input: -------------------------------------------------------------------------------- 1 | if 1 < 2 then print 'hi' else print 'no' end 2 | -------------------------------------------------------------------------------- /docs/cfg/for2.input: -------------------------------------------------------------------------------- 1 | for i=1,10 do for j = 10,1,-1 do print(i,j) end end 2 | -------------------------------------------------------------------------------- /docs/cfg/repeat1.input: -------------------------------------------------------------------------------- 1 | repeat print('forever') break until true return 2 | -------------------------------------------------------------------------------- /docs/cfg/for3.input: -------------------------------------------------------------------------------- 1 | for i=1,10 do for j = 10,1,-1 do for k=1,3,2 do print(i,j,k) end end end 2 | -------------------------------------------------------------------------------- /docs/cfg/if3.input: -------------------------------------------------------------------------------- 1 | if a < b then print 'case 1' elseif a==b then print 'case2' elseif a >= b then print 'case 3' else print 'last case' end 2 | -------------------------------------------------------------------------------- /src/chibicc/README.md: -------------------------------------------------------------------------------- 1 | This is a modified version of https://github.com/rui314/chibicc. 2 | 3 | ``` 4 | MIT License 5 | 6 | Copyright (c) 2019 Rui Ueyama 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/sieve_aot.sh: -------------------------------------------------------------------------------- 1 | ../build/trun --gen-C -main mymain -f sieve_lib.lua > sieve_lib.c 2 | gcc -O2 -shared -fpic sieve_lib.c -o sieve_lib.so 3 | ../../ravi/build/ravi sieve_aot.lua -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 8 3 | UseTab: Always 4 | BreakBeforeBraces: Linux 5 | AllowShortIfStatementsOnASingleLine: false 6 | IndentCaseLabels: false 7 | ColumnLimit: 120 -------------------------------------------------------------------------------- /examples/matrixmul_aot.sh: -------------------------------------------------------------------------------- 1 | ../build/trun --gen-C -main mymain -f matrixmul_lib.lua > matrixmul_lib.c 2 | gcc -O2 -shared -fpic matrixmul_lib.c -o matrixmul_lib.so 3 | ../../ravi/build/ravi matrixmul_aot.lua -------------------------------------------------------------------------------- /src/chibicc/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 2 3 | UseTab: Never 4 | BreakBeforeBraces: Attach 5 | AllowShortIfStatementsOnASingleLine: false 6 | IndentCaseLabels: false 7 | ColumnLimit: 120 -------------------------------------------------------------------------------- /examples/gaussian2_aot.sh: -------------------------------------------------------------------------------- 1 | ../build/trun --gen-C -main mymain -f gaussian2_lib.lua > gaussian2_lib.c 2 | gcc -O2 -shared -fpic gaussian2_lib.c -o gaussian2_lib.so 3 | ../../ravi/build/ravi gaussian2_aot.lua 4 | -------------------------------------------------------------------------------- /docs/cfg/if4-goto.input: -------------------------------------------------------------------------------- 1 | local a, b, c, d, e if a == b then goto l1 elseif a == c then goto l2 elseif a == d then goto l2 else if a == e then goto l3 else goto l3 end end ::l1:: print 'hello' ::l2:: ::l3:: ::l4:: 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | cmake_install.cmake 5 | install_manifest.txt 6 | CTestTestfile.cmake 7 | build 8 | .idea 9 | cmake-build-debug 10 | .vscode 11 | tests/results.out 12 | .vs -------------------------------------------------------------------------------- /docs/cfg/log5.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 |
L0
LOADGLOBAL {b} {T(0)}
NOT {T(0)} {T(1)}
RET {T(1)} {L1}
>]; 8 | L0 -> L1 9 | } 10 | -------------------------------------------------------------------------------- /examples/sieve_aot.lua: -------------------------------------------------------------------------------- 1 | local sieve = package.load_ravi_lib('sieve_lib.so', 'mymain') 2 | assert(sieve and type(sieve) == 'function') 3 | 4 | local t1 = os.clock() 5 | local count = sieve() 6 | local t2 = os.clock() 7 | print("time taken ", t2-t1) 8 | print(count) 9 | assert(count == 1899) 10 | print 'Ok' -------------------------------------------------------------------------------- /tests/input/t07_concat.in: -------------------------------------------------------------------------------- 1 | local a = b .. c 2 | # 3 | local hello = 'hello ' .. 'world' 4 | # 5 | local y = "ab"..(1).."cd"..(1.5) 6 | # 7 | local x = 1. 8 | local y = x .. ' test ' .. x 9 | # 10 | local x = 1. 11 | local y = x .. ' test ' .. x 12 | local z = 2. 13 | local a = x+1 .. ' ' .. y .. ' ' .. z+2 -------------------------------------------------------------------------------- /utils/tokenstr.h: -------------------------------------------------------------------------------- 1 | #include "tokenmacro.h" 2 | /* ORDER TokenType */ 3 | static const char *const luaX_tokens[] = { 4 | #define TKSTR1(name) #name, 5 | #define TKSTR2(name, sym) #sym, 6 | #define TKSTR3(name, sym) #sym #sym, 7 | TKDEF(TKSTR1, TKSTR2, TKSTR3) 8 | #undef TKSTR1 9 | #undef TKSTR2 10 | #undef TKSTR3 11 | }; -------------------------------------------------------------------------------- /utils/tokenenum.h: -------------------------------------------------------------------------------- 1 | #include "tokenmacro.h" 2 | enum TokenType { 3 | TOK_OFS = 256, 4 | #define TKENUM1(name) TOK_##name, 5 | #define TKENUM2(name, sym) TOK_##name, 6 | #define TKENUM3(name, sym) TOK_##name, 7 | TKDEF(TKENUM1, TKENUM2, TKENUM3) 8 | #undef TKENUM1 9 | #undef TKENUM2 10 | #undef TKENUM3 11 | FIRST_RESERVED = TOK_OFS +1, 12 | LAST_RESERVED = TOK_while - TOK_OFS 13 | }; -------------------------------------------------------------------------------- /tests/expected/tgraph.expected: -------------------------------------------------------------------------------- 1 | digraph { 2 | L0 -> L1 3 | L1 -> L2 4 | } 5 | digraph { 6 | L0 -> L1 7 | L0 -> L5 8 | L1 -> L2 9 | L1 -> L4 10 | L2 -> L3 11 | L2 -> L6 12 | L3 -> L4 13 | L3 -> L2 14 | L4 -> L5 15 | L4 -> L1 16 | L6 -> L3 17 | } 18 | IDOM[0] = 0 19 | IDOM[1] = 0 20 | IDOM[2] = 1 21 | IDOM[3] = 1 22 | IDOM[4] = 3 23 | IDOM[5] = 1 24 | IDOM[6] = 5 25 | IDOM[7] = 5 26 | IDOM[8] = 5 27 | Ok 28 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Debian / Ubuntu version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 2 | ARG VARIANT=buster 3 | FROM mcr.microsoft.com/vscode/devcontainers/cpp:dev-${VARIANT} 4 | 5 | # [Optional] Uncomment this section to install additional packages. 6 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 7 | # && apt-get -y install --no-install-recommends 8 | -------------------------------------------------------------------------------- /examples/matrixmul_aot.lua: -------------------------------------------------------------------------------- 1 | local f = package.load_ravi_lib('matrixmul_lib.so', 'mymain') 2 | assert(f and type(f) == 'function') 3 | matrix = f() 4 | assert(matrix and type(matrix) == 'table') 5 | local n = 1000; 6 | n = math.floor(n/2) * 2; 7 | local t1 = os.clock() 8 | local a = matrix.mul(matrix.gen(n), matrix.gen(n)) 9 | local t2 = os.clock() 10 | print("time taken ", t2-t1) 11 | print(a[n/2+1][n//2+1]) 12 | print 'Ok' 13 | -------------------------------------------------------------------------------- /tests/input/t02_locals.in: -------------------------------------------------------------------------------- 1 | local i = 0 2 | # 3 | local i, j = 1, 2 4 | # 5 | local a = a 6 | # 7 | local a = b 8 | # 9 | local a, b = x, y 10 | # 11 | local a: integer return a+3 12 | # 13 | local i: integer; return t[i/5] 14 | # 15 | local t: integer[]; return t[0] 16 | # 17 | local t: table local len: integer = #t return len 18 | # 19 | _soft = rawget(_G, "_soft") or false 20 | Z[1][2] = false or true 21 | local _g 22 | r()[a].name = _g and 'Dibyendu' or 'majumdar' 23 | # 24 | 25 | -------------------------------------------------------------------------------- /examples/sieve_lib.lua: -------------------------------------------------------------------------------- 1 | local i: integer, k: integer, prime: integer, count: integer 2 | local flags: integer[] = table.intarray(8190) 3 | 4 | for iter=0,100000 do 5 | count = 0 6 | for i=0,8190 do 7 | flags[i] = 1 8 | end 9 | for i=0,8190 do 10 | if flags[i] == 1 then 11 | prime = i + i + 3; 12 | for k = i + prime, 8190, prime do 13 | flags[k] = 0 14 | end 15 | count = count + 1 16 | end 17 | end 18 | end 19 | return count -------------------------------------------------------------------------------- /docs/cfg/repeat1.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L3}
>]; 6 | L0 -> L3 7 | L3 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 | 12 |
L3
LOADGLOBAL {print} {T(0)}
CALL {T(0), 'forever' Ks(0)} {T(0..)}
BR {L4}
>]; 13 | L3 -> L4 14 | L4 [shape=none, margin=0, label=< 15 | 16 | 17 |
L4
RET {L1}
>]; 18 | L4 -> L1 19 | } 20 | -------------------------------------------------------------------------------- /docs/cfg/log1.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 |
L0
LOADGLOBAL {a} {T(1)}
MOV {T(1)} {T(0)}
CBR {T(0)} {L2, L3}
>]; 8 | L0 -> L2 9 | L0 -> L3 10 | L2 [shape=none, margin=0, label=< 11 | 12 | 13 | 14 | 15 |
L2
LOADGLOBAL {b} {T(1)}
MOV {T(1)} {T(0)}
BR {L3}
>]; 16 | L2 -> L3 17 | L3 [shape=none, margin=0, label=< 18 | 19 | 20 |
L3
RET {T(0)} {L1}
>]; 21 | L3 -> L1 22 | } 23 | -------------------------------------------------------------------------------- /docs/cfg/log2.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 |
L0
LOADGLOBAL {a} {T(1)}
MOV {T(1)} {T(0)}
CBR {T(0)} {L3, L2}
>]; 8 | L0 -> L3 9 | L0 -> L2 10 | L2 [shape=none, margin=0, label=< 11 | 12 | 13 | 14 | 15 |
L2
LOADGLOBAL {b} {T(1)}
MOV {T(1)} {T(0)}
BR {L3}
>]; 16 | L2 -> L3 17 | L3 [shape=none, margin=0, label=< 18 | 19 | 20 |
L3
RET {T(0)} {L1}
>]; 21 | L3 -> L1 22 | } 23 | -------------------------------------------------------------------------------- /tests/input/t01_globals.in: -------------------------------------------------------------------------------- 1 | print 'hello' 2 | # 3 | x = 1 4 | # 5 | x = 1, 2 6 | # 7 | x[1] = 1 8 | # 9 | x[1] = b 10 | # 11 | x[1][1] = b 12 | # 13 | x() 14 | # 15 | return x[1] 16 | # 17 | return x() 18 | # 19 | return x[1]() 20 | # 21 | return x[1]:name() 22 | # 23 | return x[1]:name(1,2) 24 | # 25 | return x(), y() 26 | # 27 | return y(x()) 28 | # 29 | x()[1] 30 | # 31 | x()[1](a,b) 32 | # 33 | x,y = 1,2 34 | # 35 | x,y = f() 36 | # 37 | x[1],y[1],c,d = 1,z() 38 | # 39 | x[1][2],y[1],c,d = 1,z() 40 | # 41 | x,y = y,x 42 | # 43 | x,y,z = z,y,x 44 | # 45 | i = 3; i, a[i] = i+1, 20 46 | # 47 | x(y(a[10],5,z()))[1] = 9 48 | # 49 | return f()[1] 50 | # 51 | return x.y[1] 52 | # 53 | return (b or a)+1 == 2 and (10 or a)+1 == 11 54 | # 55 | -------------------------------------------------------------------------------- /tests/input/t06_close.in: -------------------------------------------------------------------------------- 1 | # 2 | local z, a do (function() a =1 end)() end 3 | # 4 | function add3(x) 5 | return function (y) 6 | return function (z) return x + y + z end 7 | end 8 | end 9 | # 10 | local a = {} 11 | local x = 10 12 | for i = 1, 2 do 13 | local j = i 14 | a[i] = function() return x + j end 15 | end 16 | x = 20 17 | # 18 | local x = 1 19 | do 20 | for i = 1,10 do 21 | local j = i; 22 | (function () x = x + j end)() 23 | if i == 5 then break end 24 | end 25 | end 26 | print(x) 27 | # 28 | function A(a) 29 | local function B(b) 30 | local function C(c) 31 | return c + g + b + a 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /.devcontainer/base.Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Debian / Ubuntu version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 2 | ARG VARIANT=buster 3 | FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT} 4 | 5 | # Install needed packages. Use a separate RUN statement to add your own dependencies. 6 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 7 | && apt-get -y install build-essential cmake cppcheck valgrind clang lldb llvm gdb \ 8 | && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 9 | 10 | # [Optional] Uncomment this section to install additional OS packages. 11 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 12 | # && apt-get -y install --no-install-recommends 13 | -------------------------------------------------------------------------------- /tests/input/t08_genericfor.in: -------------------------------------------------------------------------------- 1 | for k,v in pairs(t) do 2 | print(k,v) 3 | end 4 | # 5 | local values = {} 6 | for k,v in pairs({ name='Dibyendu', surname='Majumdar' }) do 7 | local key = k 8 | local value = v 9 | values[key] = value 10 | end 11 | # 12 | local values = {} 13 | for k, v in pairs({name='Dibyendu'}) do 14 | for k, v in pairs({surname='Majumdar'}) do 15 | assert(k == 'surname') 16 | assert(v == 'Majumdar') 17 | end 18 | assert(k == 'name') 19 | assert(v == 'Dibyendu') 20 | end 21 | # 22 | local function allcases (n: integer) 23 | for i = 1, n - 1 do 24 | for _, v1 in ipairs(allcases(i)) do 25 | for _, v2 in ipairs(allcases(n - i)) do 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /docs/cfg/while1.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 |
L2
CBR {true} {L3, L4}
>]; 11 | L2 -> L3 12 | L2 -> L4 13 | L3 [shape=none, margin=0, label=< 14 | 15 | 16 | 17 | 18 |
L3
LOADGLOBAL {print} {T(0)}
CALL {T(0), 'forever' Ks(0)} {T(0..)}
BR {L2}
>]; 19 | L3 -> L2 20 | L4 [shape=none, margin=0, label=< 21 | 22 | 23 |
L4
BR {L1}
>]; 24 | L4 -> L1 25 | } 26 | -------------------------------------------------------------------------------- /docs/cfg/if1.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 |
L2
LIii {1 Kint(0), 2 Kint(1)} {T(0)}
CBR {T(0)} {L3, L4}
>]; 12 | L2 -> L3 13 | L2 -> L4 14 | L3 [shape=none, margin=0, label=< 15 | 16 | 17 | 18 | 19 |
L3
LOADGLOBAL {print} {T(1)}
CALL {T(1), 'hi' Ks(2)} {T(1..)}
BR {L4}
>]; 20 | L3 -> L4 21 | L4 [shape=none, margin=0, label=< 22 | 23 | 24 |
L4
BR {L1}
>]; 25 | L4 -> L1 26 | } 27 | -------------------------------------------------------------------------------- /docs/cfg/while2.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 |
L2
LT {local(a, 0), 10 Kint(0)} {T(0)}
CBR {T(0)} {L3, L4}
>]; 12 | L2 -> L3 13 | L2 -> L4 14 | L3 [shape=none, margin=0, label=< 15 | 16 | 17 | 18 | 19 |
L3
ADD {local(a, 0), 1 Kint(1)} {T(0)}
MOV {T(0)} {local(a, 0)}
BR {L2}
>]; 20 | L3 -> L2 21 | L4 [shape=none, margin=0, label=< 22 | 23 | 24 |
L4
BR {L1}
>]; 25 | L4 -> L1 26 | } 27 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "C++", 3 | "build": { 4 | "dockerfile": "Dockerfile", 5 | // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 6 | "args": { "VARIANT": "debian-10" } 7 | }, 8 | "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], 9 | 10 | // Set *default* container specific settings.json values on container create. 11 | "settings": { 12 | "terminal.integrated.shell.linux": "/bin/bash" 13 | }, 14 | 15 | // Add the IDs of extensions you want installed when the container is created. 16 | "extensions": [ 17 | "ms-vscode.cpptools" 18 | ], 19 | 20 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 21 | // "forwardPorts": [], 22 | 23 | // Use 'postCreateCommand' to run commands after the container is created. 24 | // "postCreateCommand": "gcc -v", 25 | 26 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 27 | "remoteUser": "vscode" 28 | } -------------------------------------------------------------------------------- /tests/input/t05_upvals.in: -------------------------------------------------------------------------------- 1 | local a = 1 2 | function f() 3 | return function() 4 | return function() 5 | a = a + 1 6 | return a 7 | end 8 | end, a 9 | end 10 | # 11 | return function() 12 | local a = 1 13 | local function y() 14 | return function() return a end 15 | end 16 | local a = 5 17 | local function z() 18 | return function() return a end 19 | end 20 | return y, z 21 | end 22 | # 23 | local x 24 | do 25 | local y = 12 26 | goto l1 27 | ::l2:: x = x + 1; goto l3 28 | ::l1:: x = y; goto l2 29 | end 30 | ::l3:: ::l3_1:: assert(x == 13) 31 | # 32 | local msgs = {} 33 | function Message (m) 34 | if not _nomsg then 35 | print(m) 36 | msgs[#msgs+1] = string.sub(m, 3, -3) 37 | end 38 | end 39 | # 40 | local b 41 | local function z(p1,p2) 42 | do 43 | local x,y 44 | end 45 | local a,x,c,y 46 | local function t() 47 | a =1 48 | return y + x + function() 49 | b = 2 50 | return x * y end 51 | end 52 | end 53 | # 54 | local _ENV = {}; return function() x = 1 end 55 | # -------------------------------------------------------------------------------- /utils/tokenmacro.h: -------------------------------------------------------------------------------- 1 | /* This is derived from LuaJit implementation */ 2 | #define TKDEF(_, __, ___) \ 3 | _(and) _(break) _(do) _(else) _(elseif) _(end) \ 4 | _(false) _(for) _(function) _(goto) _(if) _(in) _(local) \ 5 | _(defer) _(C__decl) _(C__unsafe) _(C__new) /* Ravi extensions */ \ 6 | _(nil) _(not) _(or) \ 7 | _(repeat) _(return) _(then) _(true) _(until) _(while) \ 8 | /* other terminal symbols */ \ 9 | ___(IDIV, /) __(CONCAT, ..) __(DOTS, ...) __(EQ, ==) \ 10 | __(GE, >=) __(LE, <=) __(NE, ~=) __(SHL, <<) \ 11 | __(SHR, >>) __(DBCOLON, ::) \ 12 | /** RAVI extensions */ \ 13 | __(TO_INTEGER, @integer) __(TO_NUMBER, @number) \ 14 | __(TO_INTARRAY, @integer[]) __(TO_NUMARRAY, @number[]) \ 15 | __(TO_TABLE, @table) __(TO_STRING, @string) __(TO_CLOSURE, @closure) __(EOS, ) \ 16 | /* Tokens below this populate the seminfo */ \ 17 | __(FLT, ) __(INT, ) __(NAME, ) __(STRING, ) -------------------------------------------------------------------------------- /docs/cfg/if2.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 |
L2
LIii {1 Kint(0), 2 Kint(1)} {T(0)}
CBR {T(0)} {L3, L4}
>]; 12 | L2 -> L3 13 | L2 -> L4 14 | L3 [shape=none, margin=0, label=< 15 | 16 | 17 | 18 | 19 |
L3
LOADGLOBAL {print} {T(1)}
CALL {T(1), 'hi' Ks(2)} {T(1..)}
BR {L5}
>]; 20 | L3 -> L5 21 | L4 [shape=none, margin=0, label=< 22 | 23 | 24 | 25 | 26 |
L4
LOADGLOBAL {print} {T(2)}
CALL {T(2), 'no' Ks(3)} {T(2..)}
BR {L5}
>]; 27 | L4 -> L5 28 | L5 [shape=none, margin=0, label=< 29 | 30 | 31 |
L5
BR {L1}
>]; 32 | L5 -> L1 33 | } 34 | -------------------------------------------------------------------------------- /docs/cfg/log3.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 |
L0
LOADGLOBAL {a} {T(1)}
MOV {T(1)} {T(0)}
CBR {T(0)} {L3, L2}
>]; 8 | L0 -> L3 9 | L0 -> L2 10 | L2 [shape=none, margin=0, label=< 11 | 12 | 13 | 14 | 15 |
L2
LOADGLOBAL {b} {T(2)}
MOV {T(2)} {T(1)}
CBR {T(1)} {L4, L5}
>]; 16 | L2 -> L4 17 | L2 -> L5 18 | L3 [shape=none, margin=0, label=< 19 | 20 | 21 |
L3
RET {T(0)} {L1}
>]; 22 | L3 -> L1 23 | L4 [shape=none, margin=0, label=< 24 | 25 | 26 | 27 | 28 |
L4
LOADGLOBAL {c} {T(2)}
MOV {T(2)} {T(1)}
BR {L5}
>]; 29 | L4 -> L5 30 | L5 [shape=none, margin=0, label=< 31 | 32 | 33 | 34 |
L5
MOV {T(1)} {T(0)}
BR {L3}
>]; 35 | L5 -> L3 36 | } 37 | -------------------------------------------------------------------------------- /docs/cfg/log4.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 |
L0
LOADGLOBAL {a} {T(2)}
MOV {T(2)} {T(1)}
CBR {T(1)} {L4, L5}
>]; 8 | L0 -> L4 9 | L0 -> L5 10 | L2 [shape=none, margin=0, label=< 11 | 12 | 13 | 14 | 15 |
L2
LOADGLOBAL {c} {T(1)}
MOV {T(1)} {T(0)}
BR {L3}
>]; 16 | L2 -> L3 17 | L3 [shape=none, margin=0, label=< 18 | 19 | 20 |
L3
RET {T(0)} {L1}
>]; 21 | L3 -> L1 22 | L4 [shape=none, margin=0, label=< 23 | 24 | 25 | 26 | 27 |
L4
LOADGLOBAL {b} {T(2)}
MOV {T(2)} {T(1)}
BR {L5}
>]; 28 | L4 -> L5 29 | L5 [shape=none, margin=0, label=< 30 | 31 | 32 | 33 |
L5
MOV {T(1)} {T(0)}
CBR {T(0)} {L3, L2}
>]; 34 | L5 -> L3 35 | L5 -> L2 36 | } 37 | -------------------------------------------------------------------------------- /examples/matrixmul_lib.lua: -------------------------------------------------------------------------------- 1 | local matrix = {} 2 | 3 | function matrix.T(a: table) 4 | local m: integer, n: integer, x: table = #a, #a[1], {}; 5 | for i = 1, n do 6 | local xi: number[] = table.numarray(n, 0.0) 7 | x[i] = xi 8 | for j = 1, m do xi[j] = @number (a[j][i]) end 9 | end 10 | return x; 11 | end 12 | 13 | function matrix.mul(a: table, b: table) 14 | assert(@integer(#a[1]) == #b); 15 | local m: integer, n: integer, p: integer, x: table = #a, #a[1], #b[1], {}; 16 | local c: table = matrix.T(b); -- transpose for efficiency 17 | for i = 1, m do 18 | local xi: number[] = table.numarray(p, 0.0) 19 | x[i] = xi 20 | for j = 1, p do 21 | local sum: number, ai: number[], cj: number[] = 0.0, @number[](a[i]), @number[](c[j]); 22 | -- for luajit, caching c[j] or not makes no difference; lua is not so clever 23 | for k = 1, n do sum = sum + ai[k] * cj[k] end 24 | xi[j] = sum; 25 | end 26 | end 27 | return x; 28 | end 29 | 30 | function matrix.gen(n: integer) 31 | local a: table, tmp: number = {}, 1. / n / n; 32 | for i = 1, n do 33 | local ai: number[] = table.numarray(n, 0.0) 34 | a[i] = ai 35 | for j = 1, n do 36 | ai[j] = tmp * (i - j) * (i + j - 2) 37 | end 38 | end 39 | return a; 40 | end 41 | return matrix 42 | -------------------------------------------------------------------------------- /tests/input/t04_stmts_1.in: -------------------------------------------------------------------------------- 1 | ::L1:: a = 1; goto L1; return 2 | # 3 | ::l1:: do goto l1; x = 1; ::l1:: z = 2 end y = 1; goto l1 4 | # 5 | #goto l1; do ::l1:: end 6 | # 7 | return function (a) ::L2:: if not(a < 10) then goto L1 end; a = a + 1; 8 | goto L2; ::L1:: end 9 | # 10 | for i=1,10 do print(i) end 11 | # 12 | for i=10,1,-1 do print(i) end 13 | # 14 | while true do print('forever') end 15 | # 16 | repeat print('forever') brek until true 17 | # 18 | repeat print('forever') break until true return 19 | # 20 | for i =1,10 do if i == 2 then break end end return 21 | # 22 | return function (a) while a < 10 do a = a + 1 end end 23 | # 24 | return function (a, b, c, d, e) 25 | if a == b then goto l1 26 | elseif a == c then goto l2 27 | elseif a == d then goto l2 28 | else if a == e then goto l3 29 | else goto l3 30 | end 31 | end 32 | ::l1:: ::l2:: ::l3:: ::l4:: 33 | end 34 | # 35 | return function () 36 | local sum 37 | for j = 1,500 do 38 | sum = 0.0 39 | for k = 1,10000 do 40 | sum = sum + 1.0/(k*k) 41 | end 42 | end 43 | return sum 44 | end 45 | # 46 | local x 47 | do 48 | local y = 12 49 | goto l1 50 | ::l2:: x = x + 1; goto l3 51 | ::l1:: x = y; goto l2 52 | end 53 | ::l3:: ::l3_1:: assert(x == 13) 54 | # 55 | 56 | -------------------------------------------------------------------------------- /tests/input/t03_types.in: -------------------------------------------------------------------------------- 1 | function foo(bar: integer[], zee: number[], ...) end 2 | # 3 | function foo(bar: number, zee: string) end 4 | # 5 | function foo(bar: closure, zee: My.User.Type) end 6 | # 7 | return @integer 1 8 | # 9 | return @string 'hello' 10 | # 11 | return @table {} 12 | # 13 | return @integer[] {} 14 | # 15 | return @number[] {} 16 | # 17 | return @closure function() end 18 | # 19 | return @number 54.4 20 | # 21 | return @User.Type a 22 | # 23 | return @integer[] {5, 4} 24 | # 25 | return @number[] {4.0, 5.4} 26 | # 27 | local a: integer return a+3 28 | # 29 | local i: integer; return t[i/5] 30 | # 31 | local t: integer[]; return t[0] 32 | # 33 | local t: integer[] if (t[1] == 5) then return true end return false 34 | # 35 | local t: table local len: integer = #t return len 36 | # 37 | return function(t: table, i: integer) i = #t end 38 | # 39 | function matmul(a: table, b: table) 40 | assert(@integer(#a[1]) == #b); 41 | local m: integer, n: integer, p: integer, x: table = #a, #a[1], #b[1], {}; 42 | local c: table = matrix.T(b); -- transpose for efficiency 43 | for i = 1, m do 44 | local xi: number[] = table.numarray(p, 0.0) 45 | x[i] = xi 46 | for j = 1, p do 47 | local sum: number, ai: number[], cj: number[] = 0.0, @number[](a[i]), @number[](c[j]); 48 | -- for luajit, caching c[j] or not makes no difference; lua is not so clever 49 | for k = 1, n do sum = sum + ai[k] * cj[k] end 50 | xi[j] = sum; 51 | end 52 | end 53 | return x 54 | end 55 | return matmul 56 | # 57 | return #{} 58 | # 59 | return #(@integer[]{}) 60 | # 61 | -------------------------------------------------------------------------------- /examples/gaussian2_aot.lua: -------------------------------------------------------------------------------- 1 | local function comp(a: number[], b: number[]) 2 | local abs = math.abs 3 | for i = 1, #a do 4 | if abs(a[i] - b[i]) > 1e-10 then 5 | return false 6 | end 7 | end 8 | return true 9 | end 10 | 11 | local f = package.load_ravi_lib('gaussian2_lib.so', 'mymain') 12 | assert(f and type(f) == 'function') 13 | local glib = f() 14 | assert(glib and type(glib) == 'table') 15 | 16 | local gaussian_solve = glib.gaussian_solve 17 | assert(gaussian_solve and type(gaussian_solve) == 'function') 18 | local A: number[] = { 4,8,-4; 2,5,1; 1,5,10 } 19 | local b: number[] = { 3,8,5 } 20 | 21 | -- control (LAPACK solve) 22 | local expectedx: number[] = { 1,-1,1 } 23 | local x:number[] = gaussian_solve(A, b, 3, 3) 24 | 25 | print('expected ', table.unpack(expectedx)) 26 | print('got ', table.unpack(x)) 27 | assert(comp(x, expectedx)) 28 | 29 | local A: number[] = { 2,6,4; 1,-1,3; -1,-9,1 } 30 | local b: number[] = { 3,7,5 } 31 | 32 | local expectedx: number[] = { 0,2,-1 } 33 | x = gaussian_solve(A, b, 3, 3) 34 | 35 | print('expected ', table.unpack(expectedx)) 36 | print('got ', table.unpack(x)) 37 | assert(comp(x, expectedx)) 38 | 39 | local A: number[] = { 0,1,2,1; 1,1,2,2; 1,2,4,1; 1,1,0,1 } 40 | local b: number[] = { 1,-1,-1,2 } 41 | 42 | local expectedx: number[] = { -1.25, 2.25, -0.75, -0.5 } 43 | x = gaussian_solve(A, b, 4, 4) 44 | 45 | print('expected ', table.unpack(expectedx)) 46 | print('got ', table.unpack(x)) 47 | assert(comp(x, expectedx)) 48 | 49 | print 'Ok' 50 | --ravi.dumplua(gaussian_solve) 51 | --ravi.dumplua(partial_pivot) 52 | -------------------------------------------------------------------------------- /src/cfg.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_CFG_H 25 | #define ravicomp_CFG_H 26 | 27 | #include "linearizer.h" 28 | 29 | #include 30 | 31 | int raviX_construct_cfg(Proc *proc); 32 | void raviX_output_cfg(Proc *proc, FILE *fp); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /docs/cfg/gencfg.sh: -------------------------------------------------------------------------------- 1 | doit() 2 | { 3 | code=$1 4 | name=$2 5 | echo "$code" > $name.input 6 | ../../build/trun --noastdump --noirdump --nocodump --remove-unreachable-blocks "$code" > $name.gv 7 | dot -Tsvg -o$name.svg $name.gv 8 | } 9 | 10 | doit "if 1 < 2 then print 'hi' end" "if1" 11 | doit "if 1 < 2 then print 'hi' else print 'no' end" "if2" 12 | doit "if a < b then print 'case 1' elseif a==b then print 'case2' elseif a >= b then print 'case 3' else print 'last case' end" "if3" 13 | # doit "function x(a, b, c, d, e) if a == b then goto l1 elseif a == c then goto l2 elseif a == d then goto l2 else if a == e then goto l3 else goto l3 end end ::l1:: ::l2:: ::l3:: ::l4:: end" "if4-goto" 14 | doit "local a, b, c, d, e if a == b then goto l1 elseif a == c then goto l2 elseif a == d then goto l2 else if a == e then goto l3 else goto l3 end end ::l1:: print 'hello' ::l2:: ::l3:: ::l4::" "if4-goto" 15 | 16 | doit "for i=1,10 do print(i) end" "for1" 17 | doit "for i=1,10 do for j = 10,1,-1 do print(i,j) end end" "for2" 18 | doit "for i=1,10 do for j = 10,1,-1 do for k=1,3,2 do print(i,j,k) end end end" "for3" 19 | doit "for i =1,10 do if i == 2 then break end end" "for4" 20 | 21 | doit "return a and b" "log1" 22 | doit "return a or b" "log2" 23 | doit "return a or b and c" "log3" 24 | doit "return a and b or c" "log4" 25 | doit "return not b" "log5" 26 | 27 | doit "while true do print('forever') end" "while1" 28 | # doit "function x(a) while a < 10 do a = a + 1 end end" "while2" 29 | doit "local a while a < 10 do a = a + 1 end" "while2" 30 | doit "repeat print('forever') break until true return" "repeat1" 31 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Sources 2 | 3 | * `lexer.c` - derived from Lua 5.3 lexer but modified to work as a standalone lexer 4 | * `parser.c` - responsible for generating abstract syntax tree (AST) - consumes lexer output 5 | * `ast_printer.c` - responsible for printing out the AST 6 | * `ast_walker.c` - API for walking the AST 7 | * `ast_simplify.c` - responsible for simplifications done on AST such as constant folding 8 | * `ast_lower.c` - AST transformations - converts generic for loop to while loop 9 | * `typechecker.c` - responsible for performing typechecking and assigning types to various things. Runs on the AST 10 | * `linearizer.c` - responsible for generating linear intermediate code (linear IR) from the AST 11 | * `cfg.c` - responsible for constructing a control flow graph from the output of the linearizer 12 | * `dominator.c` - implementation of dominator tree calculation - not used yet 13 | * `dataflow_framework.c` - a framework for calculating dataflow equations - not used yet 14 | * `opt_unusedcode.c` - a simple optimization pass that deletes unreachable basic blocks 15 | * `codegen.c` - responsible for generating C code from the linear IR 16 | 17 | ## Utilities 18 | 19 | * `allocate.c` - memory allocator 20 | * `fnv_hash.c` - string hashing function 21 | * `hash_table.c` - hash table 22 | * `set.c` - set data structure 23 | * `ptrlist.c` - a hybrid array/linked list data structure 24 | * `membuf.c` - dynamic memory buffer that supports formatted input - used to build strings incrementally 25 | * `graph.c` - simple graph data structure used to generate control flow graph 26 | * `bitset.c` - bitset data structure 27 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | env: 8 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 9 | BUILD_TYPE: Debug 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [windows-latest, ubuntu-latest, macOS-latest] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Create Build Environment 23 | run: cmake -E make_directory ${{runner.workspace}}/build 24 | 25 | - name: Configure CMake 26 | # Use a bash shell so we can use the same syntax for environment variable 27 | # access regardless of the host operating system 28 | shell: bash 29 | working-directory: ${{runner.workspace}}/build 30 | # Note the current convention is to use the -S and -B options here to specify source 31 | # and build directories, but this is only available with CMake 3.13 and higher. 32 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 33 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 34 | 35 | - name: Build 36 | working-directory: ${{runner.workspace}}/build 37 | shell: bash 38 | # Execute the build. You can specify a specific target with "--target " 39 | run: cmake --build . --config $BUILD_TYPE 40 | 41 | - name: Test 42 | if: runner.os != 'Windows' 43 | shell: bash 44 | run: | 45 | cd tests 46 | sh truntests.sh ${{runner.workspace}}/build/trun 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/cfg/for1.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
L0
MOV {1 Kint(0)} {Tint(1)}
MOV {10 Kint(1)} {Tint(2)}
MOV {1 Kint(0)} {Tint(3)}
LIii {0 Kint(2), Tint(3)} {Tint(4)}
SUBii {Tint(1), Tint(3)} {Tint(1)}
BR {L2}
>]; 11 | L0 -> L2 12 | L2 [shape=none, margin=0, label=< 13 | 14 | 15 | 16 |
L2
ADDii {Tint(1), Tint(3)} {Tint(1)}
CBR {Tint(4)} {L3, L4}
>]; 17 | L2 -> L3 18 | L2 -> L4 19 | L3 [shape=none, margin=0, label=< 20 | 21 | 22 | 23 |
L3
LEii {Tint(2), Tint(1)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 24 | L3 -> L6 25 | L3 -> L5 26 | L4 [shape=none, margin=0, label=< 27 | 28 | 29 | 30 |
L4
LIii {Tint(1), Tint(2)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 31 | L4 -> L6 32 | L4 -> L5 33 | L5 [shape=none, margin=0, label=< 34 | 35 | 36 | 37 | 38 | 39 |
L5
MOV {Tint(1)} {Tint(0)}
LOADGLOBAL {print} {T(0)}
CALL {T(0), Tint(0)} {T(0..)}
BR {L2}
>]; 40 | L5 -> L2 41 | L6 [shape=none, margin=0, label=< 42 | 43 | 44 |
L6
BR {L1}
>]; 45 | L6 -> L1 46 | } 47 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_COMMON_H 25 | #define ravicomp_COMMON_H 26 | 27 | #include 28 | 29 | typedef uint32_t nodeId_t; /* The type used to identify nodes in CFG */ 30 | 31 | /* We have two distinguished basic blocks in every proc */ 32 | enum { 33 | ENTRY_BLOCK = 0, 34 | EXIT_BLOCK = 1 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/optimizer.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_OPTIMIZER_H 25 | #define ravicomp_OPTIMIZER_H 26 | 27 | /** 28 | * Remove blocks that are unreachable. Blocks ae logically deleted by removing 29 | * all instructions, rather than being physically removed. 30 | */ 31 | extern int raviX_remove_unreachable_blocks(LinearizerState *linearizer); 32 | 33 | #endif -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains examples of code that can be compiled AOT and run via Ravi. 4 | Note that these examples can also be JIT compiled - for an example of how that works, please see [21_matrixmul.lua](https://github.com/dibyendumajumdar/ravi/blob/master/tests/comptests/inputs/21_matrixmul.lua). 5 | 6 | The examples assume that Ravi and Ravi Compiler have been built in parallel folders, and the 7 | executables are available in `build` folders. 8 | 9 | You can run these examples as follows on a Linux host: 10 | 11 | ``` 12 | sh matrixmul_aot.sh 13 | sh sieve_aot.sh 14 | ``` 15 | 16 | The following explains the steps, using the matrix multiplication example. 17 | 18 | ``` 19 | ../build/trun --gen-C -main mymain -f matrixmul_lib.lua > matrixmul_lib.c 20 | ``` 21 | 22 | Above generates C code that represents the Ravi code in `matrixmul_lib.lua`. 23 | The C code will contain a function named `mymain` that will be the equivalent of the Lua chunk. 24 | 25 | ``` 26 | gcc -O2 -shared -fpic matrixmul_lib.c -o matrixmul_lib.so 27 | ``` 28 | 29 | Above creates a shared library. 30 | 31 | ``` 32 | ../../ravi/build/ravi matrixmul_aot.lua 33 | ``` 34 | 35 | Above step loads the shared library and then executes it similar to the way a Lua chunk is executed when it is loaded. 36 | 37 | ``` 38 | local f = package.load_ravi_lib('matrixmul_lib.so', 'mymain') 39 | assert(f and type(f) == 'function') 40 | local matrix = f() 41 | ``` 42 | 43 | The `package.load_ravi_lib()` function is a special Ravi extension that loads the `mymain` function from the shared 44 | library. Note that this function behaves like a Lua load, rather than C functions that are normally loaded by Lua. 45 | 46 | `matrix` will contain the table that was returned by the `matrixmul_lib.lua` script. 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/dominator.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_DOMINATOR_H 25 | #define ravicomp_DOMINATOR_H 26 | 27 | #include "graph.h" 28 | 29 | #include 30 | 31 | typedef struct DominatorTree DominatorTree; 32 | 33 | DominatorTree *raviX_new_dominator_tree(Graph *g); 34 | void raviX_calculate_dominator_tree(DominatorTree *state); 35 | void raviX_destroy_dominator_tree(DominatorTree *state); 36 | void raviX_dominator_tree_output(DominatorTree *tree, FILE *fp); 37 | 38 | #endif -------------------------------------------------------------------------------- /docs/cfg/log5.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | LOADGLOBAL {b} {T(0)} 17 | NOT {T(0)} {T(1)} 18 | RET {T(1)} {L1} 19 | 20 | 21 | 22 | 23 | L1 24 | 25 | L1 26 | 27 | 28 | 29 | L0->L1 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/codegen.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_CODEGEN_H 25 | #define ravicomp_CODEGEN_H 26 | 27 | #include "ravi_compiler.h" 28 | #include "ravi_api.h" 29 | #include "linearizer.h" 30 | 31 | // FIXME this needs to be obtained from luaconf.h 32 | #define RAVI_DEFER_STATEMENT 33 | 34 | RAVICOMP_EXPORT int raviX_generate_C(LinearizerState *linearizer, TextBuffer *mb, struct Ravi_CompilerInterface *ravi_interface); 35 | RAVICOMP_EXPORT void raviX_generate_C_tofile(LinearizerState *linearizer, const char *mainfunc, FILE *fp); 36 | 37 | #endif -------------------------------------------------------------------------------- /tests/tchibicc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ravi_alloc.h" 3 | #include 4 | 5 | static void create_allocator(C_MemoryAllocator *allocator) { 6 | allocator->arena = create_mspace(0, 0); 7 | allocator->realloc = mspace_realloc; 8 | allocator->calloc = mspace_calloc; 9 | allocator->free = mspace_free; 10 | allocator->create_arena = create_mspace; 11 | allocator->destroy_arena = destroy_mspace; 12 | } 13 | 14 | static void destroy_allocator(C_MemoryAllocator *allocator) { 15 | allocator->destroy_arena(allocator->arena); 16 | } 17 | 18 | static void printout(void *userdata, char *key, int keylen, void *val) 19 | { 20 | printf("name: %d %.*s\n", keylen, keylen, key); 21 | } 22 | 23 | int main(int argc, const char *argv[]) 24 | { 25 | char buffer[100] = {0}; 26 | char buffer2[100] = {0}; 27 | const char *code = "" \ 28 | "typedef struct {\n" \ 29 | " char *data;\n" \ 30 | " int len;\n" \ 31 | "} Str;\n"; 32 | strncpy(buffer, code, sizeof buffer); 33 | C_MemoryAllocator allocator; 34 | create_allocator(&allocator); 35 | C_Parser parser = {0}; 36 | C_parser_init(&parser, &allocator); 37 | C_Token *tok = C_tokenize_buffer(&parser, buffer); 38 | C_convert_pp_tokens(&parser, tok); 39 | C_Scope scope = {0}; 40 | scope.vars.allocator = parser.memory_allocator; 41 | scope.tags.allocator = parser.memory_allocator; 42 | C_Obj *obj = C_parse(&scope, &parser, tok); 43 | hashmap_foreach(&scope.vars, printout, NULL); 44 | C_create_function(&scope, &parser, "dummy"); 45 | hashmap_foreach(&scope.vars, printout, NULL); 46 | const char *snippet = "{ Str s; s.data = \"hello world\"; s.len = sizeof \"hello world\"; }\n"; 47 | strncpy(buffer2, snippet, sizeof buffer2); 48 | tok = C_tokenize_buffer(&parser, buffer2); 49 | C_convert_pp_tokens(&parser, tok); 50 | parser.embedded_mode = true; 51 | C_Node *node = C_parse_compound_statement(&scope, &parser, tok); 52 | C_parser_destroy(&parser); 53 | destroy_allocator(&allocator); 54 | return 0; 55 | } -------------------------------------------------------------------------------- /src/fnv_hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2009 Intel Corporation 3 | * Copyright © 2014 Broadcom 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice (including the next 13 | * paragraph) shall be included in all copies or substantial portions of the 14 | * Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | * 24 | * Authors: 25 | * Eric Anholt 26 | */ 27 | 28 | /* Quick FNV-1 hash implementation based on: 29 | * http://www.isthe.com/chongo/tech/comp/fnv/ 30 | */ 31 | 32 | #ifndef ravicomp_FNV_HASH_H 33 | #define ravicomp_FNV_HASH_H 34 | 35 | #include 36 | #include 37 | 38 | uint32_t fnv1_hash_string(const char *key); 39 | uint32_t fnv1_hash_data(const void *data, size_t size); 40 | 41 | int string_key_equals(const void *a, const void *b); 42 | 43 | #define hash_table_create_for_string() \ 44 | raviX_hash_table_create((uint32_t (*)(const void *key))fnv1_hash_string, \ 45 | string_key_equals) 46 | 47 | #define set_create_for_string() \ 48 | set_create((uint32_t (*)(const void *key))fnv1_hash_string, \ 49 | string_key_equals) 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/dataflow_framework.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_DATAFLOW_FRAMEWORK_H 25 | #define ravicomp_DATAFLOW_FRAMEWORK_H 26 | 27 | #include "graph.h" 28 | #include 29 | 30 | /* 31 | * Data Flow Analysis framework. 32 | * The Join/Transfer functions should return 1 if they made any changes else 0. 33 | */ 34 | extern void raviX_solve_dataflow( 35 | Graph *g, 36 | bool forward_p, /* Set to true for forward data flow */ 37 | int (*join_function)(void *userdata, nodeId_t, bool init), /* Join/Meet operator - if init is true reset the bitsets */ 38 | int (*transfer_function)(void *userdata, nodeId_t), /* transfer function */ 39 | void *userdata); /* pointer to user data, will be passed to join/transfer functions */ 40 | 41 | 42 | #endif -------------------------------------------------------------------------------- /docs/cfg/for4.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
L0
MOV {1 Kint(0)} {Tint(1)}
MOV {10 Kint(1)} {Tint(2)}
MOV {1 Kint(0)} {Tint(3)}
LIii {0 Kint(2), Tint(3)} {Tint(4)}
SUBii {Tint(1), Tint(3)} {Tint(1)}
BR {L2}
>]; 11 | L0 -> L2 12 | L2 [shape=none, margin=0, label=< 13 | 14 | 15 | 16 |
L2
ADDii {Tint(1), Tint(3)} {Tint(1)}
CBR {Tint(4)} {L3, L4}
>]; 17 | L2 -> L3 18 | L2 -> L4 19 | L3 [shape=none, margin=0, label=< 20 | 21 | 22 | 23 |
L3
LEii {Tint(2), Tint(1)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 24 | L3 -> L6 25 | L3 -> L5 26 | L4 [shape=none, margin=0, label=< 27 | 28 | 29 | 30 |
L4
LIii {Tint(1), Tint(2)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 31 | L4 -> L6 32 | L4 -> L5 33 | L5 [shape=none, margin=0, label=< 34 | 35 | 36 | 37 |
L5
MOV {Tint(1)} {Tint(0)}
BR {L7}
>]; 38 | L5 -> L7 39 | L6 [shape=none, margin=0, label=< 40 | 41 | 42 |
L6
BR {L1}
>]; 43 | L6 -> L1 44 | L7 [shape=none, margin=0, label=< 45 | 46 | 47 | 48 |
L7
EQii {Tint(0), 2 Kint(3)} {T(0)}
CBR {T(0)} {L8, L9}
>]; 49 | L7 -> L8 50 | L7 -> L9 51 | L8 [shape=none, margin=0, label=< 52 | 53 | 54 |
L8
BR {L6}
>]; 55 | L8 -> L6 56 | L9 [shape=none, margin=0, label=< 57 | 58 | 59 |
L9
BR {L2}
>]; 60 | L9 -> L2 61 | } 62 | -------------------------------------------------------------------------------- /src/chibicc/chibicc_strings.c: -------------------------------------------------------------------------------- 1 | /* 2 | Adapted from https://github.com/rui314/chibicc 3 | 4 | MIT License 5 | 6 | Copyright (c) 2019 Rui Ueyama 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | 28 | #include "chibicc.h" 29 | 30 | void strarray_push(C_MemoryAllocator *allocator, StringArray *arr, char *s) { 31 | if (!arr->data) { 32 | arr->data = allocator->calloc(allocator->arena, 8, sizeof(char *)); 33 | arr->capacity = 8; 34 | } 35 | 36 | if (arr->capacity == arr->len) { 37 | arr->data = allocator->realloc(allocator->arena, arr->data, sizeof(char *) * arr->capacity * 2); 38 | arr->capacity *= 2; 39 | for (int i = arr->len; i < arr->capacity; i++) 40 | arr->data[i] = NULL; 41 | } 42 | 43 | arr->data[arr->len++] = s; 44 | } 45 | 46 | #if 0 47 | // Takes a printf-style format string and returns a formatted string. 48 | char *format(char *fmt, ...) { 49 | char *buf; 50 | size_t buflen; 51 | FILE *out = open_memstream(&buf, &buflen); 52 | 53 | va_list ap; 54 | va_start(ap, fmt); 55 | vfprintf(out, fmt, ap); 56 | va_end(ap); 57 | fclose(out); 58 | return buf; 59 | } 60 | #endif -------------------------------------------------------------------------------- /tests/tcommon.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_TCOMMON_H 25 | #define ravicomp_TCOMMON_H 26 | 27 | #include "ravi_compiler.h" 28 | 29 | struct arguments { 30 | const char *filename; 31 | const char *code; 32 | unsigned typecheck : 1, linearize : 1, astdump : 1, irdump : 1, cfgdump : 1, codump : 1, simplify_ast : 1, 33 | remove_unreachable_blocks: 1, gen_C: 1, opt_upvalue: 1, table_ast : 1; 34 | const char *mainfunc; /* name of the main function in generated code, only applies if gen_C is on */ 35 | }; 36 | extern void parse_arguments(struct arguments *args, int argc, const char *argv[]); 37 | extern void destroy_arguments(struct arguments *args); 38 | extern const char *read_file(const char *filename); 39 | 40 | extern void create_allocator(C_MemoryAllocator *allocator); 41 | extern void destroy_allocator(C_MemoryAllocator *allocator); 42 | 43 | #endif -------------------------------------------------------------------------------- /tests/input/t00_exprs.in: -------------------------------------------------------------------------------- 1 | return 2 | # 3 | return 1 4 | # 5 | return 42, 4.2, true, 'hello' 6 | # 7 | return 1+2 8 | # 9 | return 2^3-5*4 10 | # 11 | return 1+1 12 | # 13 | return 1+1+1 14 | # 15 | return 2-3/5*4 16 | # 17 | return 4.2//5 18 | # 19 | return 0.0 20 | # 21 | return 0 22 | # 23 | return -0//1 24 | # 25 | return 3^-1 26 | # 27 | return (1 + 1)^(50 + 50) 28 | # 29 | return (-2)^(31 - 2) 30 | # 31 | return (-3^0 + 5) // 3.0 32 | # 33 | return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD 34 | # 35 | return ~(~0xFF0 | 0xFF0) 36 | # 37 | return ~~-100024.0 38 | # 39 | return ((100 << 6) << -4) >> 2 40 | # 41 | return 2^3^2 == 2^(3^2) 42 | # 43 | return 2^3*4 == (2^3)*4 44 | # 45 | return 2.0^-2 == 1/4 and -2^- -2 == - - -4 46 | # 47 | return not nil and 2 and not(2>3 or 3<2) 48 | # 49 | return -3-1-5 == 0+0-9 50 | # 51 | return -2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0 52 | # 53 | return 2*1+3/3 == 3 and 1+2 .. 3*1 == '33' 54 | # 55 | return not(2+1 > 3*1) and 'a'..'b' > 'a' 56 | # 57 | return '7' .. 3 << 1 == 146 58 | # 59 | return 10 >> 1 .. '9' == 0 60 | # 61 | return 10 | 1 .. '9' == 27 62 | # 63 | return 0xF0 | 0xCC ~ 0xAA & 0xFD == 0xF4 64 | # 65 | return 0xFD & 0xAA ~ 0xCC | 0xF0 == 0xF4 66 | # 67 | return 0xF0 & 0x0F + 1 == 0x10 68 | # 69 | return 3^4//2^3//5 == 2 70 | # 71 | return not ((true or false) and nil) 72 | # 73 | return true or false and nil 74 | # 75 | return (((1 or false) and true) or false) == true 76 | # 77 | return (((nil and true) or false) and true) == false 78 | # 79 | return -(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75 80 | # 81 | return ((2<3) or 1) == true and (2<3 and 4) == 4 82 | # 83 | return (x>y) and x or y == 2 84 | # 85 | return {1,2,3} 86 | # 87 | return 1 and 2 88 | # 89 | return 3 and 4 and 5 90 | # 91 | return 1 or 2 92 | # 93 | return 3 or 4 or 5 94 | # 95 | return -3+4*5//2^3^2//9+4%10/3 == (-3)+(((4*5)//(2^(3^2)))//9)+((4%10)/3) 96 | # 97 | return -3%5 == 2 and -3+5 == 2 98 | # 99 | return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 100 | # 101 | return -((2^8 + -(-1)) % 8)//2 * 4 - 3 102 | # 103 | return function () 104 | (function () end){f()} 105 | end 106 | # 107 | return (f()) 108 | # 109 | return {g()}, f(g()) -------------------------------------------------------------------------------- /tests/truntests.sh: -------------------------------------------------------------------------------- 1 | command=$1 2 | set -e 3 | 4 | echo "testing t00_exprs" 5 | $command -f input/t00_exprs.in > results.out 6 | #cp results.out expected/t00_exprs.expected 7 | diff expected/t00_exprs.expected results.out 8 | rm results.out 9 | 10 | echo "testing t01_globals" 11 | $command -f input/t01_globals.in > results.out 12 | #cp results.out expected/t01_globals.expected 13 | diff expected/t01_globals.expected results.out 14 | rm results.out 15 | 16 | echo "testing t02_locals" 17 | $command -f input/t02_locals.in > results.out 18 | #cp results.out expected/t02_locals.expected 19 | diff expected/t02_locals.expected results.out 20 | rm results.out 21 | 22 | echo "testing t03_types" 23 | $command -f input/t03_types.in > results.out 24 | #cp results.out expected/t03_types.expected 25 | diff expected/t03_types.expected results.out 26 | rm results.out 27 | 28 | echo "testing t04_stmts_1" 29 | $command -f input/t04_stmts_1.in > results.out 30 | #cp results.out expected/t04_stmts_1.expected 31 | diff expected/t04_stmts_1.expected results.out 32 | rm results.out 33 | 34 | echo "testing t05_upvals" 35 | $command -f input/t05_upvals.in > results.out 36 | #cp results.out expected/t05_upvals.expected 37 | diff expected/t05_upvals.expected results.out 38 | rm results.out 39 | 40 | echo "testing t06_close" 41 | $command -f input/t06_close.in > results.out 42 | #cp results.out expected/t06_close.expected 43 | diff expected/t06_close.expected results.out 44 | rm results.out 45 | 46 | echo "testing t07_concat" 47 | $command -f input/t07_concat.in > results.out 48 | #cp results.out expected/t07_concat.expected 49 | diff expected/t07_concat.expected results.out 50 | rm results.out 51 | 52 | echo "testing t08_genericfor" 53 | $command -f input/t08_genericfor.in > results.out 54 | #cp results.out expected/t08_genericfor.expected 55 | diff expected/t08_genericfor.expected results.out 56 | rm results.out 57 | 58 | echo "testing t09_bugs" 59 | $command -f input/t09_bugs.in > results.out 60 | #cp results.out expected/t09_bugs.expected 61 | diff expected/t09_bugs.expected results.out 62 | rm results.out 63 | 64 | echo "testing t10_embed_C" 65 | $command -f input/t10_embed_C.in > results.out 66 | #cp results.out expected/t10_embed_C.expected 67 | diff expected/t10_embed_C.expected results.out 68 | rm results.out -------------------------------------------------------------------------------- /src/membuf.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_MEMBUF_H 25 | #define ravicomp_MEMBUF_H 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | extern void raviX_buffer_add_bool(TextBuffer *mb, bool value); 36 | extern void raviX_buffer_add_int(TextBuffer *mb, int value); 37 | extern void raviX_buffer_add_longlong(TextBuffer *mb, int64_t value); 38 | extern void raviX_buffer_add_char(TextBuffer *mb, char c); 39 | extern void raviX_buffer_add_double(TextBuffer *mb, double d); // Outputs the double as string using ryu 40 | 41 | /* Following add and remove raw bytes */ 42 | 43 | /* Unchecked - user must first resize */ 44 | static inline void raviX_buffer_addc(TextBuffer *mb, int c) 45 | { 46 | mb->buf[mb->pos++] = (char)c; 47 | assert(mb->pos < mb->capacity); 48 | } 49 | static inline void raviX_buffer_remove(TextBuffer *mb, int i) { mb->pos -= i; } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/fnv_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2009 Intel Corporation 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | * 23 | * Authors: 24 | * Eric Anholt 25 | */ 26 | 27 | /* Quick FNV-1a hash implementation based on: 28 | * http://www.isthe.com/chongo/tech/comp/fnv/ 29 | * 30 | * FNV-1a may not be the best hash out there -- Jenkins's lookup3 is supposed 31 | * to be quite good, and it may beat FNV. But FNV has the advantage that it 32 | * involves almost no code. 33 | */ 34 | #include "fnv_hash.h" 35 | 36 | #include 37 | 38 | uint32_t 39 | fnv1_hash_string(const char *key) 40 | { 41 | uint32_t hash = 2166136261ul; 42 | const uint8_t *bytes = (uint8_t *)key; 43 | 44 | while (*bytes != 0) { 45 | hash ^= *bytes; 46 | hash = hash * 0x01000193; 47 | bytes++; 48 | } 49 | 50 | return hash; 51 | } 52 | 53 | uint32_t 54 | fnv1_hash_data(const void *data, size_t size) 55 | { 56 | uint32_t hash = 2166136261ul; 57 | const uint8_t *bytes = (uint8_t *)data; 58 | 59 | while (size-- != 0) { 60 | hash ^= *bytes; 61 | hash = hash * 0x01000193; 62 | bytes++; 63 | } 64 | 65 | return hash; 66 | } 67 | 68 | int 69 | string_key_equals(const void *a, const void *b) 70 | { 71 | return strcmp((const char *)a, (const char *)b) == 0; 72 | } 73 | -------------------------------------------------------------------------------- /docs/cfg/if3.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 | 12 | 13 |
L2
LOADGLOBAL {a} {T(0)}
LOADGLOBAL {b} {T(1)}
LT {T(0), T(1)} {T(2)}
CBR {T(2)} {L5, L3}
>]; 14 | L2 -> L5 15 | L2 -> L3 16 | L3 [shape=none, margin=0, label=< 17 | 18 | 19 | 20 | 21 | 22 |
L3
LOADGLOBAL {a} {T(1)}
LOADGLOBAL {b} {T(0)}
EQ {T(1), T(0)} {T(3)}
CBR {T(3)} {L6, L4}
>]; 23 | L3 -> L6 24 | L3 -> L4 25 | L4 [shape=none, margin=0, label=< 26 | 27 | 28 | 29 | 30 | 31 |
L4
LOADGLOBAL {a} {T(0)}
LOADGLOBAL {b} {T(1)}
LE {T(1), T(0)} {T(4)}
CBR {T(4)} {L7, L8}
>]; 32 | L4 -> L7 33 | L4 -> L8 34 | L5 [shape=none, margin=0, label=< 35 | 36 | 37 | 38 | 39 |
L5
LOADGLOBAL {print} {T(0)}
CALL {T(0), 'case 1' Ks(0)} {T(0..)}
BR {L9}
>]; 40 | L5 -> L9 41 | L6 [shape=none, margin=0, label=< 42 | 43 | 44 | 45 | 46 |
L6
LOADGLOBAL {print} {T(1)}
CALL {T(1), 'case2' Ks(1)} {T(1..)}
BR {L9}
>]; 47 | L6 -> L9 48 | L7 [shape=none, margin=0, label=< 49 | 50 | 51 | 52 | 53 |
L7
LOADGLOBAL {print} {T(5)}
CALL {T(5), 'case 3' Ks(2)} {T(5..)}
BR {L9}
>]; 54 | L7 -> L9 55 | L8 [shape=none, margin=0, label=< 56 | 57 | 58 | 59 | 60 |
L8
LOADGLOBAL {print} {T(6)}
CALL {T(6), 'last case' Ks(3)} {T(6..)}
BR {L9}
>]; 61 | L8 -> L9 62 | L9 [shape=none, margin=0, label=< 63 | 64 | 65 |
L9
BR {L1}
>]; 66 | L9 -> L1 67 | } 68 | -------------------------------------------------------------------------------- /src/allocate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This version is part of the Ravi Compiler project. 3 | * Copyright (C) 2017-2022 Dibyendu Majumdar 4 | */ 5 | 6 | #include "allocate.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void *raviX_malloc(size_t size) 14 | { 15 | void *ptr; 16 | ptr = malloc(size); 17 | if (!ptr) { 18 | fprintf(stderr, "out of memory\n"); 19 | exit(1); 20 | } 21 | return ptr; 22 | } 23 | 24 | void *raviX_calloc(size_t nmemb, size_t size) 25 | { 26 | void *ptr; 27 | ptr = calloc(nmemb, size); 28 | if (!ptr) { 29 | fprintf(stderr, "out of memory\n"); 30 | exit(1); 31 | } 32 | return ptr; 33 | } 34 | 35 | void *raviX_realloc(void *p, size_t size) 36 | { 37 | void *ptr; 38 | ptr = realloc(p, size); 39 | if (!ptr) { 40 | fprintf(stderr, "out of memory\n"); 41 | exit(1); 42 | } 43 | return ptr; 44 | } 45 | 46 | void raviX_free(void *ptr) 47 | { 48 | free(ptr); 49 | } 50 | 51 | 52 | /* 53 | Reallocate array from old_n to new_n. If new_n is 0 then array memeory is freed. 54 | If new_n is greater than old_n then old data is copied across and the 55 | additional allocated space is zeroed out so caller can rely on the extra space being 56 | initialized to zeros. 57 | */ 58 | void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n) 59 | { 60 | if (new_n == 0) { 61 | raviX_free(oldp); 62 | return NULL; 63 | } 64 | assert(new_n > old_n); 65 | size_t newsize = element_size * new_n; 66 | void *newp = realloc(oldp, newsize); 67 | if (!newp) { 68 | fprintf(stderr, "out of memory\n"); 69 | abort(); 70 | } 71 | size_t oldsize = old_n * element_size; 72 | char *p = (char *)newp; 73 | memset(p + oldsize, 0, newsize - oldsize); 74 | return newp; 75 | } 76 | 77 | /* 78 | Delete n elements starting at i from array a of size array_size, where sizeof(each element) is element_size. 79 | The freed up space will be zero initialized. 80 | */ 81 | size_t raviX_del_array_element(void *a, size_t element_size, size_t array_size, size_t i, size_t n) 82 | { 83 | assert(i + n <= array_size); 84 | char *p = (char *)a; 85 | char *dest = p + i * element_size; 86 | char const *src = p + (i + n) * element_size; 87 | size_t count = element_size * (array_size - n - i); 88 | memmove(dest, src, count); 89 | size_t new_array_size = array_size - n; 90 | size_t newsize = element_size * new_array_size; 91 | size_t oldsize = element_size * array_size; 92 | memset(p + newsize, 0, oldsize - newsize); 93 | return new_array_size; 94 | } 95 | -------------------------------------------------------------------------------- /docs/cfg/if4-goto.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 |
L0
BR {L2}
>]; 6 | L0 -> L2 7 | L2 [shape=none, margin=0, label=< 8 | 9 | 10 | 11 |
L2
EQ {local(a, 0), local(b, 1)} {T(0)}
CBR {T(0)} {L5, L3}
>]; 12 | L2 -> L5 13 | L2 -> L3 14 | L3 [shape=none, margin=0, label=< 15 | 16 | 17 | 18 |
L3
EQ {local(a, 0), local(c, 2)} {T(1)}
CBR {T(1)} {L6, L4}
>]; 19 | L3 -> L6 20 | L3 -> L4 21 | L4 [shape=none, margin=0, label=< 22 | 23 | 24 | 25 |
L4
EQ {local(a, 0), local(d, 3)} {T(2)}
CBR {T(2)} {L7, L8}
>]; 26 | L4 -> L7 27 | L4 -> L8 28 | L5 [shape=none, margin=0, label=< 29 | 30 | 31 |
L5
BR {L10}
>]; 32 | L5 -> L10 33 | L6 [shape=none, margin=0, label=< 34 | 35 | 36 |
L6
BR {L12}
>]; 37 | L6 -> L12 38 | L7 [shape=none, margin=0, label=< 39 | 40 | 41 |
L7
BR {L12}
>]; 42 | L7 -> L12 43 | L8 [shape=none, margin=0, label=< 44 | 45 | 46 |
L8
BR {L15}
>]; 47 | L8 -> L15 48 | L10 [shape=none, margin=0, label=< 49 | 50 | 51 | 52 | 53 |
L10
LOADGLOBAL {print} {T(4)}
CALL {T(4), 'hello' Ks(0)} {T(4..)}
BR {L12}
>]; 54 | L10 -> L12 55 | L12 [shape=none, margin=0, label=< 56 | 57 | 58 |
L12
BR {L19}
>]; 59 | L12 -> L19 60 | L15 [shape=none, margin=0, label=< 61 | 62 | 63 | 64 |
L15
EQ {local(a, 0), local(e, 4)} {T(3)}
CBR {T(3)} {L16, L17}
>]; 65 | L15 -> L16 66 | L15 -> L17 67 | L16 [shape=none, margin=0, label=< 68 | 69 | 70 |
L16
BR {L19}
>]; 71 | L16 -> L19 72 | L17 [shape=none, margin=0, label=< 73 | 74 | 75 |
L17
BR {L19}
>]; 76 | L17 -> L19 77 | L19 [shape=none, margin=0, label=< 78 | 79 | 80 |
L19
BR {L1}
>]; 81 | L19 -> L1 82 | } 83 | -------------------------------------------------------------------------------- /tests/input/t09_bugs.in: -------------------------------------------------------------------------------- 1 | local assert = assert 2 | return function(m: integer, n: integer) 3 | assert(m == n) 4 | print('testing') 5 | end 6 | # 7 | local abs = math.abs 8 | local function compute_pi() 9 | if abs(c) > TOL then 10 | end 11 | end 12 | -- #64 13 | local t = { name_ = "ravi" } 14 | function t:name(optional) 15 | return self.name_, optional 16 | end 17 | -- issue #66 18 | local function test_upvaluejoin() 19 | local debug = require'debug' 20 | local foo1, foo2, foo3, foo4 21 | do 22 | local a:integer, b:integer = 3, 5 23 | local c:number = 7.1 24 | foo1 = function() return a+b end 25 | foo2 = function() return b+a end 26 | foo4 = function() return c end 27 | do 28 | local a: integer = 10 29 | foo3 = function() return a+b end 30 | end 31 | end 32 | end 33 | -- issue #65 34 | do 35 | local x = function () 36 | local function createarray(values: number[]) 37 | local arr: number[] = table.numarray(#values, 0) 38 | for i=1,#values do 39 | arr[i] = values[i] 40 | end 41 | return arr 42 | end 43 | end 44 | end 45 | # 46 | do 47 | local x = function() 48 | local function tryme() 49 | local i,j = 5,6 50 | return i,j 51 | end 52 | local i:integer, j:integer = tryme() 53 | assert(i+j == 11) 54 | end 55 | x() 56 | end 57 | # 58 | do 59 | local y 60 | local z = function() 61 | local x=function() 62 | local j:number[] = {} 63 | return j 64 | end 65 | y=x() 66 | y[1] = 99.67 67 | assert(y[1], 99.67) 68 | assert(@integer (#y) == 1) 69 | end 70 | z() 71 | end 72 | # 73 | do 74 | local function test_numarray_meta() 75 | local farray : number[] = {1.1, 2.2, 3.3} 76 | setmetatable(farray, { 77 | __name = 'matrix', 78 | __tostring = function() return '{' .. table.concat(farray, ",") .. '}' end 79 | }) 80 | assert(ravitype(farray) == 'matrix') 81 | assert(tostring(farray) == '{1.1,2.2,3.3}') 82 | end 83 | assert(pcall(test_numarray_meta)); 84 | end 85 | # 86 | local function test() 87 | return 1.0 88 | end 89 | --print(test()) 90 | return test() 91 | # codegen bug assign empty string to local 92 | function z() 93 | local t = '' 94 | print(t) 95 | return t 96 | end 97 | # 98 | function test() 99 | local myPlayer = getMyPlayer() 100 | if ( myPlayer:Alive() ) then 101 | return true 102 | end 103 | return false 104 | end 105 | function getMyPlayer() 106 | local player = {} 107 | function player:Alive() 108 | return 1 109 | end 110 | return player 111 | end 112 | # 113 | local b: number 114 | local j: integer 115 | local i: integer 116 | local a:number[] 117 | a[i]=a[i]+b*a[j] 118 | # 119 | unlpack((ret2(f()))) -------------------------------------------------------------------------------- /docs/cfg/for2.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
L0
MOV {1 Kint(0)} {Tint(1)}
MOV {10 Kint(1)} {Tint(2)}
MOV {1 Kint(0)} {Tint(3)}
LIii {0 Kint(2), Tint(3)} {Tint(4)}
SUBii {Tint(1), Tint(3)} {Tint(1)}
BR {L2}
>]; 11 | L0 -> L2 12 | L2 [shape=none, margin=0, label=< 13 | 14 | 15 | 16 |
L2
ADDii {Tint(1), Tint(3)} {Tint(1)}
CBR {Tint(4)} {L3, L4}
>]; 17 | L2 -> L3 18 | L2 -> L4 19 | L3 [shape=none, margin=0, label=< 20 | 21 | 22 | 23 |
L3
LEii {Tint(2), Tint(1)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 24 | L3 -> L6 25 | L3 -> L5 26 | L4 [shape=none, margin=0, label=< 27 | 28 | 29 | 30 |
L4
LIii {Tint(1), Tint(2)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 31 | L4 -> L6 32 | L4 -> L5 33 | L5 [shape=none, margin=0, label=< 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
L5
MOV {Tint(1)} {Tint(0)}
MOV {10 Kint(1)} {Tint(7)}
MOV {1 Kint(0)} {Tint(8)}
UNMi {1 Kint(0)} {Tint(9)}
MOV {Tint(9)} {Tint(10)}
LIii {0 Kint(2), Tint(10)} {Tint(11)}
SUBii {Tint(7), Tint(10)} {Tint(7)}
BR {L7}
>]; 44 | L5 -> L7 45 | L6 [shape=none, margin=0, label=< 46 | 47 | 48 |
L6
BR {L1}
>]; 49 | L6 -> L1 50 | L7 [shape=none, margin=0, label=< 51 | 52 | 53 | 54 |
L7
ADDii {Tint(7), Tint(10)} {Tint(7)}
CBR {Tint(11)} {L8, L9}
>]; 55 | L7 -> L8 56 | L7 -> L9 57 | L8 [shape=none, margin=0, label=< 58 | 59 | 60 | 61 |
L8
LEii {Tint(8), Tint(7)} {Tint(12)}
CBR {Tint(12)} {L11, L10}
>]; 62 | L8 -> L11 63 | L8 -> L10 64 | L9 [shape=none, margin=0, label=< 65 | 66 | 67 | 68 |
L9
LIii {Tint(7), Tint(8)} {Tint(12)}
CBR {Tint(12)} {L11, L10}
>]; 69 | L9 -> L11 70 | L9 -> L10 71 | L10 [shape=none, margin=0, label=< 72 | 73 | 74 | 75 | 76 | 77 |
L10
MOV {Tint(7)} {Tint(6)}
LOADGLOBAL {print} {T(0)}
CALL {T(0), Tint(0), Tint(6)} {T(0..)}
BR {L7}
>]; 78 | L10 -> L7 79 | L11 [shape=none, margin=0, label=< 80 | 81 | 82 |
L11
BR {L2}
>]; 83 | L11 -> L2 84 | } 85 | -------------------------------------------------------------------------------- /docs/cfg/repeat1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | BR {L3} 17 | 18 | 19 | 20 | 21 | L3 22 | L3 23 | LOADGLOBAL {print} {T(0)} 24 | CALL {T(0), 'forever' Ks(0)} {T(0..)} 25 | BR {L4} 26 | 27 | 28 | 29 | 30 | L0->L3 31 | 32 | 33 | 34 | 35 | 36 | L4 37 | L4 38 | RET {L1} 39 | 40 | 41 | 42 | 43 | L3->L4 44 | 45 | 46 | 47 | 48 | 49 | L1 50 | 51 | L1 52 | 53 | 54 | 55 | L4->L1 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/set.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2009 Intel Corporation 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | * 23 | * Authors: 24 | * Eric Anholt 25 | * 26 | */ 27 | 28 | #ifndef ravicomp_SET_H 29 | #define ravicomp_SET_H 30 | 31 | #include 32 | #include 33 | 34 | typedef struct SetEntry { 35 | uint32_t hash; 36 | const void *key; 37 | } SetEntry; 38 | 39 | typedef struct Set { 40 | SetEntry *table; 41 | uint32_t (*hash_function)(const void *key); 42 | int (*key_equals_function)(const void *a, const void *b); 43 | uint32_t size; 44 | uint32_t rehash; 45 | uint32_t max_entries; 46 | uint32_t size_index; 47 | uint32_t entries; 48 | uint32_t deleted_entries; 49 | } Set; 50 | 51 | Set *raviX_set_create(uint32_t (*hash_function)(const void *key), 52 | int (*key_equals_function)(const void *a, 53 | const void *b)); 54 | void raviX_set_destroy(Set *set, 55 | void (*delete_function)(SetEntry *entry)); 56 | 57 | SetEntry *raviX_set_add(Set *set, const void *key); 58 | 59 | bool raviX_set_contains(Set *set, const void *key); 60 | 61 | void raviX_set_remove(Set *set, const void *key); 62 | 63 | SetEntry *raviX_set_search(Set *set, const void *key); 64 | 65 | void raviX_set_remove_entry(Set *set, SetEntry *entry); 66 | 67 | SetEntry *raviX_set_next_entry(Set *set, SetEntry *entry); 68 | 69 | SetEntry *raviX_set_random_entry(Set *set, 70 | int (*predicate)(SetEntry *entry)); 71 | 72 | /** 73 | * This foreach function is safe against deletion (which just replaces 74 | * an entry's data with the deleted marker), but not against insertion 75 | * (which may rehash the table, making entry a dangling pointer). 76 | */ 77 | #define set_foreach(ht, entry) \ 78 | for (entry = raviX_set_next_entry(ht, NULL); \ 79 | entry != NULL; \ 80 | entry = raviX_set_next_entry(ht, entry)) 81 | 82 | /* Alternate interfaces to reduce repeated calls to hash function. */ 83 | SetEntry *raviX_set_search_pre_hashed(Set *set, uint32_t hash, const void *key); 84 | 85 | SetEntry *raviX_set_add_pre_hashed(Set *set, uint32_t hash, const void *key); 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /include/ravi_api.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | #ifndef ravicomp_RAVIAPI_H 24 | #define ravicomp_RAVIAPI_H 25 | 26 | #include "ravi_compiler.h" 27 | #include "ravicomp_export.h" 28 | 29 | #include 30 | 31 | typedef struct Ravi_CompilerInterface { 32 | /* ------------------------ Inputs ------------------------------ */ 33 | void *context; /* Ravi supplied context, passed to debug_message/error_message callbacks */ 34 | 35 | const char *source; /* Source code to be compiled - managed by Ravi */ 36 | size_t source_len; /* Size of source code */ 37 | const char *source_name; /* Name of the source */ 38 | const char *compiler_options; /* flags to be passed to compiler */ 39 | 40 | char main_func_name[31]; /* Name of the generated function that when called will set up the Lua closure */ 41 | 42 | C_MemoryAllocator *memory_allocator; /* Memory allocator to use */ 43 | 44 | /* ------------------------- Outputs ------------------------------ */ 45 | const char *generated_code; /* Output of the compiler; call raviX_release() to free this */ 46 | 47 | /* ------------------------ Debugging and error handling ----------------------------------------- */ 48 | /* context will be passed as first parameter */ 49 | void (*debug_message)(void *context, const char *filename, long long line, const char *message); 50 | void (*error_message)(void *context, const char *message); 51 | } Ravi_CompilerInterface; 52 | 53 | /** 54 | * This is the API exposed by the Compiler itself. This function is invoked by 55 | * Ravi when it is necessary to compile some Ravi code. 56 | * @param compiler_interface The interface expected by the compiler must be setup 57 | * @return 0 for success, non-zero for failure 58 | */ 59 | RAVICOMP_EXPORT int raviX_compile(Ravi_CompilerInterface *compiler_interface); 60 | /* Releases memory etc. held by the compiler context */ 61 | RAVICOMP_EXPORT void raviX_release(Ravi_CompilerInterface *compiler_interface); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /tests/input/t10_embed_C.in: -------------------------------------------------------------------------------- 1 | C__decl [[ 2 | typedef struct MyStruct { 3 | long a; 4 | int variable_length[]; 5 | } MyStruct; 6 | ]] 7 | 8 | function demo(u: Userdata) 9 | 10 | C__unsafe(u) [[ 11 | MyStruct *s = (MyStruct *) u.ptr; 12 | ]] 13 | local u1 = C__new('MyStruct',1) 14 | 15 | end 16 | # 17 | local i: integer = 1 18 | local j: integer = 2 19 | local k: integer 20 | 21 | C__unsafe(i,j,k) [[ 22 | k = i+j; // Okay as primitive types 23 | ]] 24 | # 25 | C__decl [[ 26 | typedef struct { 27 | int i; 28 | } MyStruct; 29 | ]] 30 | 31 | local u = C__new('MyStruct', 1) 32 | 33 | local i: integer = 42 34 | local j: integer 35 | 36 | C__unsafe(i,j,u) [[ 37 | MyStruct *s = (MyStruct *) u.ptr; 38 | s->i = i; 39 | j = s->i; 40 | ]] 41 | 42 | return j 43 | # 44 | -- implement a date type using embedded C 45 | 46 | 47 | C__decl [[ 48 | typedef struct { 49 | unsigned char d; 50 | unsigned char m; 51 | short y; 52 | int serial; 53 | } Date; 54 | ]] 55 | 56 | DateFunctions = {} 57 | function DateFunctions.make_date(d: integer, m: integer, y: integer) 58 | local date = C__new('Date', 1) 59 | 60 | C__unsafe(date, d, m, y) [[ 61 | Date *dateptr = (Date *) date.ptr; 62 | dateptr->d = (unsigned char)d; 63 | dateptr->m = (unsigned char)m; 64 | dateptr->y = (short)y; 65 | y -= m <= 2; 66 | int era = (y >= 0 ? y : y - 399) / 400; 67 | unsigned yoe = (unsigned)(y - era * 400); // [0, 399] 68 | unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365] 69 | unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096] 70 | dateptr->serial = era * 146097 + (int)doe - 719468 + 25569; // +25569 adjusts the serial number to match Excel 71 | ]] 72 | 73 | return date 74 | end 75 | 76 | function DateFunctions.print_date(date: any) 77 | 78 | local d: integer 79 | local m: integer 80 | local y: integer 81 | local j: integer 82 | C__unsafe(date, d, m, y, j) [[ 83 | Date *dateptr = (Date *) date.ptr; 84 | d = dateptr->d; 85 | m = dateptr->m; 86 | y = dateptr->y; 87 | j = dateptr->serial; 88 | ]] 89 | 90 | print(d,m,y,j) 91 | end 92 | 93 | function DateFunctions.get_day(date: any) 94 | 95 | local v: integer 96 | C__unsafe(date, v) [[ 97 | Date *dateptr = (Date *) date.ptr; 98 | v = dateptr->d; 99 | ]] 100 | 101 | return v 102 | end 103 | 104 | function DateFunctions.get_month(date: any) 105 | 106 | local v: integer 107 | C__unsafe(date, v) [[ 108 | Date *dateptr = (Date *) date.ptr; 109 | v = dateptr->m; 110 | ]] 111 | 112 | return v 113 | end 114 | 115 | function DateFunctions.get_year(date: any) 116 | 117 | local v: integer 118 | C__unsafe(date, v) [[ 119 | Date *dateptr = (Date *) date.ptr; 120 | v = dateptr->y; 121 | ]] 122 | 123 | return v 124 | end 125 | 126 | function DateFunctions.get_serial(date: any) 127 | 128 | local v: integer 129 | C__unsafe(date, v) [[ 130 | Date *dateptr = (Date *) date.ptr; 131 | v = dateptr->serial; 132 | ]] 133 | 134 | return v 135 | end 136 | 137 | local d = DateFunctions.make_date(1,1,1900) 138 | DateFunctions.print_date(d) 139 | assert(1900 == DateFunctions.get_year(d)) 140 | assert(1 == DateFunctions.get_month(d)) 141 | assert(1 == DateFunctions.get_day(d)) 142 | assert(2 == DateFunctions.get_serial(d)) -------------------------------------------------------------------------------- /src/hash_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2009 Intel Corporation 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | * 23 | * Authors: 24 | * Eric Anholt 25 | * 26 | */ 27 | 28 | #ifndef ravicomp_HASH_TABLE_H 29 | #define ravicomp_HASH_TABLE_H 30 | 31 | //#ifdef __cplusplus 32 | //extern "C" { 33 | //#endif 34 | 35 | #include 36 | 37 | typedef struct HashEntry { 38 | uint32_t hash; 39 | const void *key; 40 | void *data; 41 | } HashEntry; 42 | 43 | typedef struct HashTable { 44 | HashEntry *table; 45 | uint32_t (*hash_function)(const void *key); 46 | int (*key_equals_function)(const void *a, const void *b); 47 | uint32_t size; 48 | uint32_t rehash; 49 | uint32_t max_entries; 50 | uint32_t size_index; 51 | uint32_t entries; 52 | uint32_t deleted_entries; 53 | } HashTable; 54 | 55 | HashTable * 56 | raviX_hash_table_create(uint32_t (*hash_function)(const void *key), 57 | int (*key_equals_function)(const void *a, 58 | const void *b)); 59 | void 60 | raviX_hash_table_destroy(HashTable *ht, 61 | void (*delete_function)(HashEntry *entry)); 62 | 63 | HashEntry * 64 | raviX_hash_table_insert(HashTable *ht, const void *key, void *data); 65 | 66 | HashEntry * 67 | raviX_hash_table_search(HashTable *ht, const void *key); 68 | 69 | void 70 | raviX_hash_table_remove(HashTable *ht, const void *key); 71 | 72 | void 73 | raviX_hash_table_remove_entry(HashTable *ht, HashEntry *entry); 74 | 75 | HashEntry * 76 | raviX_hash_table_next_entry(HashTable *ht, 77 | HashEntry *entry); 78 | 79 | //HashEntry * 80 | //hash_table_random_entry(HashTable *ht, 81 | // int (*predicate)(HashEntry *entry)); 82 | 83 | /** 84 | * This foreach function is safe against deletion (which just replaces 85 | * an entry's data with the deleted marker), but not against insertion 86 | * (which may rehash the table, making entry a dangling pointer). 87 | */ 88 | #define hash_table_foreach(ht, entry) \ 89 | for (entry = raviX_hash_table_next_entry(ht, NULL); \ 90 | entry != NULL; \ 91 | entry = raviX_hash_table_next_entry(ht, entry)) 92 | 93 | /* Alternate interfaces to reduce repeated calls to hash function. */ 94 | HashEntry * 95 | raviX_hash_table_search_pre_hashed(HashTable *ht, 96 | uint32_t hash, 97 | const void *key); 98 | 99 | HashEntry * 100 | raviX_hash_table_insert_pre_hashed(HashTable *ht, 101 | uint32_t hash, 102 | const void *key, void *data); 103 | 104 | 105 | //#ifdef __cplusplus 106 | //} /* extern C */ 107 | //#endif 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /docs/cfg/log2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | LOADGLOBAL {a} {T(1)} 17 | MOV {T(1)} {T(0)} 18 | CBR {T(0)} {L3, L2} 19 | 20 | 21 | 22 | 23 | L3 24 | L3 25 | RET {T(0)} {L1} 26 | 27 | 28 | 29 | 30 | L0->L3 31 | 32 | 33 | 34 | 35 | 36 | L2 37 | L2 38 | LOADGLOBAL {b} {T(1)} 39 | MOV {T(1)} {T(0)} 40 | BR {L3} 41 | 42 | 43 | 44 | 45 | L0->L2 46 | 47 | 48 | 49 | 50 | 51 | L1 52 | 53 | L1 54 | 55 | 56 | 57 | L3->L1 58 | 59 | 60 | 61 | 62 | 63 | L2->L3 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/cfg/log1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | LOADGLOBAL {a} {T(1)} 17 | MOV {T(1)} {T(0)} 18 | CBR {T(0)} {L2, L3} 19 | 20 | 21 | 22 | 23 | L2 24 | L2 25 | LOADGLOBAL {b} {T(1)} 26 | MOV {T(1)} {T(0)} 27 | BR {L3} 28 | 29 | 30 | 31 | 32 | L0->L2 33 | 34 | 35 | 36 | 37 | 38 | L3 39 | L3 40 | RET {T(0)} {L1} 41 | 42 | 43 | 44 | 45 | L0->L3 46 | 47 | 48 | 49 | 50 | 51 | L2->L3 52 | 53 | 54 | 55 | 56 | 57 | L1 58 | 59 | L1 60 | 61 | 62 | 63 | L3->L1 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/cfg.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | /* Build CFG */ 25 | 26 | 27 | #include "graph.h" 28 | #include "cfg.h" 29 | 30 | #include 31 | 32 | /* Recursively create control flow graph for each proc 33 | * Return 0 on success 34 | */ 35 | int raviX_construct_cfg(Proc *proc) 36 | { 37 | Graph *g = raviX_init_graph(ENTRY_BLOCK, EXIT_BLOCK, proc, proc->linearizer->compiler_state->allocator); 38 | for (unsigned i = 0; i < proc->node_count; i++) { 39 | BasicBlock *block = proc->nodes[i]; 40 | Instruction *insn = raviX_last_instruction(block); 41 | if (insn == NULL) 42 | continue; 43 | if (insn->opcode == op_br || insn->opcode == op_cbr || insn->opcode == op_ret) { 44 | Pseudo *pseudo; 45 | FOR_EACH_PTR(insn->targets, Pseudo, pseudo) 46 | { 47 | assert(pseudo->type == PSEUDO_BLOCK); 48 | raviX_add_edge(g, block->index, pseudo->block->index); 49 | } 50 | END_FOR_EACH_PTR(pseudo) 51 | } else { 52 | return 1; 53 | } 54 | } 55 | proc->cfg = g; 56 | Proc *childproc; 57 | FOR_EACH_PTR(proc->procs, Proc, childproc) 58 | { 59 | if (raviX_construct_cfg(childproc) != 0) 60 | return 1; 61 | } 62 | END_FOR_EACH_PTR(childproc) 63 | return 0; 64 | } 65 | 66 | struct CfgArg { 67 | FILE *fp; 68 | Proc *proc; 69 | }; 70 | 71 | static void output_node(void *arg, Graph *g, uint32_t nodeid) 72 | { 73 | struct CfgArg *myargs = (struct CfgArg *)arg; 74 | FILE *fp = myargs->fp; 75 | Proc *proc = myargs->proc; 76 | GraphNodeList *successors = raviX_successors(raviX_graph_node(g, nodeid)); 77 | if (!successors) 78 | return; 79 | BasicBlock *block = proc->nodes[nodeid]; 80 | if (raviX_ptrlist_size((const PtrList *)block->insns) > 0) { 81 | TextBuffer buf; 82 | raviX_buffer_init(&buf, 1024); 83 | raviX_output_basic_block_as_table(proc, block, &buf); 84 | fprintf(fp, "L%d [shape=none, margin=0, label=<%s>];\n", nodeid, raviX_buffer_data(&buf)); 85 | raviX_buffer_free(&buf); 86 | } 87 | for (unsigned i = 0; i < raviX_node_list_size(successors); i++) { 88 | fprintf(fp, "L%d -> L%d\n", nodeid, raviX_node_list_at(successors, i)); 89 | } 90 | Proc *childproc; 91 | FOR_EACH_PTR(proc->procs, Proc, childproc) { raviX_output_cfg(childproc, fp); } 92 | END_FOR_EACH_PTR(childproc) 93 | } 94 | 95 | void raviX_output_cfg(Proc *proc, FILE *fp) 96 | { 97 | Graph *g = proc->cfg; 98 | if (!g) 99 | return; 100 | fprintf(fp, "digraph Proc%d {\n", proc->id); 101 | struct CfgArg args = {fp, proc}; 102 | raviX_for_each_node(g, output_node, &args); 103 | fprintf(fp, "}\n"); 104 | } -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## Sources 2 | 3 | * `trun.c` - a new driver for executing tests - handles multiple cases in a single input file. Runs lexer, parser, typechecker, linearizer, CFG generation, etc. and then dumps it all out on `stdout`. 4 | * `tgraph.c` - basic smoke test for graph data structure. 5 | * `tastwalk.c` - demonstrates how to write AST walking; it does not do anything but just walks the AST silently. 6 | * `tmisc.c` - miscellaneous internal tests. 7 | 8 | ## Running tests 9 | 10 | At the moment we run `tparse` or `trun` on inputs and compare the output as saved in `expected` folder. This also acts as a regression test because if anything changes then the test fails. 11 | 12 | ## `trun` 13 | 14 | The `trun` utility has the following interface. 15 | 16 | ``` 17 | trun [string | -f filename] [--notypecheck] [--nolinearize] [--noastdump] [--noirdump] [--nocodump] [--nocfgdump] [--simplify-ast] [--opt-upvalues] [--table-ast] [--remove-unreachable-blocks] [--gen-C] [-main main_function_name] 18 | ``` 19 | 20 | The options have the following meanings: 21 | 22 | * `-f filename` - input file. The input should consist of chunks of code separated by a line containing just `#`. See `t00_exprs.in` in the input folder. 23 | * `--notypecheck` - omits the type checking step 24 | * `--nolinearize` - omits creating the linear IR 25 | * `--noastdump` - stops output of AST 26 | * `--noirdump` - stops output of the linear IR 27 | * `--nocodump` - stops output of the input code chunk 28 | * `--nocfgdump` - stops output of the CFG 29 | * `--simplify-ast` - performs simplifications on the AST such as constant folding 30 | * `--remove-unreachable-blocks` - performs a step to remove unreachable blocks 31 | * `--opt-upvalues` - experimental feature to replace upvalues with constants when upvalue refers to a constant 32 | * `--table-ast` - dumps ast in a Ravi code format using functions and tables 33 | * `--gen-C` - generates C code that is suitable for JIT or AOT compilation for Ravi 34 | * `-main ` - allows naming of the main function in generated C code 35 | 36 | The CFG output is generated in the format supported by the `dot` command in `graphviz`. 37 | 38 | Currently, all output will be produced to `stdout`. 39 | 40 | Example. 41 | 42 | ``` 43 | trun "print 'hello world'" 44 | ``` 45 | 46 | Output generated: 47 | 48 | ``` 49 | print 'hello world' 50 | function() 51 | --[expression statement start] 52 | --[expression list start] 53 | --[suffixed expr start] any 54 | --[primary start] any 55 | print --global symbol any 56 | --[primary end] 57 | --[suffix list start] 58 | --[function call start] any 59 | ( 60 | 'hello world' 61 | ) 62 | --[function call end] 63 | --[suffix list end] 64 | --[suffixed expr end] 65 | --[expression list end] 66 | --[expression statement end] 67 | end 68 | function() 69 | --[expression statement start] 70 | --[expression list start] 71 | --[suffixed expr start] any 72 | --[primary start] any 73 | print --global symbol any 74 | --[primary end] 75 | --[suffix list start] 76 | --[function call start] any 77 | ( 78 | 'hello world' 79 | ) 80 | --[function call end] 81 | --[suffix list end] 82 | --[suffixed expr end] 83 | --[expression list end] 84 | --[expression statement end] 85 | end 86 | define Proc%1 87 | L0 (entry) 88 | LOADGLOBAL {print} {T(0)} 89 | CALL {T(0), 'hello world' Ks(0)} {T(0..)} 90 | BR {L1} 91 | L1 (exit) 92 | digraph Proc1 { 93 | L0 [shape=none, margin=0, label=< 94 | 95 | 96 | 97 | 98 |
L0
LOADGLOBAL {print} {T(0)}
CALL {T(0), 'hello world' Ks(0)} {T(0..)}
BR {L1}
>]; 99 | L0 -> L1 100 | } 101 | ``` 102 | 103 | -------------------------------------------------------------------------------- /src/opt_unusedcode.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | /* A pass over linearized code to eliminate unused code. 25 | * Initially only tackle unreachable basic blocks 26 | */ 27 | 28 | #include "linearizer.h" 29 | #include "cfg.h" 30 | #include "graph.h" 31 | #include "allocate.h" 32 | #include "optimizer.h" 33 | 34 | /** 35 | * Check if a basic block has 0 predecessors. If so we can remove it from the CFG. 36 | * We also remove all the instructions in the basic block 37 | */ 38 | static int process_block(LinearizerState *linearizer, Proc *proc, BasicBlock *bb) 39 | { 40 | GraphNode *node = raviX_graph_node(proc->cfg, bb->index); 41 | GraphNodeList *predecessors = raviX_predecessors(node); 42 | if (raviX_node_list_size(predecessors) != 0) { 43 | // Has predecessors so nothing to do 44 | return 0; 45 | } 46 | // No predecessor blocks, so we can remove this block 47 | GraphNodeList *successors = raviX_successors(node); 48 | uint32_t count = raviX_node_list_size(successors); 49 | if (count == 0) { 50 | // Nothing to do, but odd? 51 | // FIXME maybe assert? 52 | return 0; 53 | } 54 | // Make a copy of the successor node list as we need to change the CFG 55 | nodeId_t *nodes = (nodeId_t *) raviX_realloc_array(NULL, sizeof(nodeId_t), 0, count); 56 | for (uint32_t i = 0; i < count; i++) { 57 | nodes[i] = raviX_node_list_at(successors, i); 58 | } 59 | for (uint32_t i = 0; i < count; i++) { 60 | // Remove edge from bb to the successor node 61 | raviX_delete_edge(proc->cfg, bb->index, nodes[i]); 62 | } 63 | raviX_free(nodes); 64 | assert(raviX_node_list_size(successors) == 0); // All should be gone 65 | // Now clear out this bb 66 | // FIXME deallocate instructions 67 | raviX_ptrlist_remove_all((PtrList **)&bb->insns); 68 | // FIXME do we deallocate bb? 69 | return 1; // We changed something 70 | } 71 | 72 | static int process_proc(LinearizerState *linearizer, Proc *proc) 73 | { 74 | if (proc->cfg == NULL) { 75 | if (raviX_construct_cfg(proc) != 0) { 76 | return 1; 77 | } 78 | } 79 | int changed = 1; 80 | while (changed) { 81 | changed = 0; 82 | BasicBlock *bb; 83 | for (int i = 0; i < (int)proc->node_count; i++) { 84 | bb = proc->nodes[i]; 85 | if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) 86 | continue; 87 | changed |= process_block(linearizer, proc, bb); 88 | } 89 | } 90 | return 0; 91 | } 92 | 93 | int raviX_remove_unreachable_blocks(LinearizerState *linearizer) 94 | { 95 | Proc *proc; 96 | FOR_EACH_PTR(linearizer->all_procs, Proc, proc) 97 | { 98 | if (process_proc(linearizer, proc) != 0) 99 | return 1; 100 | } 101 | END_FOR_EACH_PTR(proc) 102 | return 0; 103 | } -------------------------------------------------------------------------------- /docs/cfg/while1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | BR {L2} 17 | 18 | 19 | 20 | 21 | L2 22 | L2 23 | CBR {true} {L3, L4} 24 | 25 | 26 | 27 | 28 | L0->L2 29 | 30 | 31 | 32 | 33 | 34 | L3 35 | L3 36 | LOADGLOBAL {print} {T(0)} 37 | CALL {T(0), 'forever' Ks(0)} {T(0..)} 38 | BR {L2} 39 | 40 | 41 | 42 | 43 | L2->L3 44 | 45 | 46 | 47 | 48 | 49 | L4 50 | L4 51 | BR {L1} 52 | 53 | 54 | 55 | 56 | L2->L4 57 | 58 | 59 | 60 | 61 | 62 | L3->L2 63 | 64 | 65 | 66 | 67 | 68 | L1 69 | 70 | L1 71 | 72 | 73 | 74 | L4->L1 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/cfg/while2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | BR {L2} 17 | 18 | 19 | 20 | 21 | L2 22 | L2 23 | LT {local(a, 0), 10 Kint(0)} {T(0)} 24 | CBR {T(0)} {L3, L4} 25 | 26 | 27 | 28 | 29 | L0->L2 30 | 31 | 32 | 33 | 34 | 35 | L3 36 | L3 37 | ADD {local(a, 0), 1 Kint(1)} {T(0)} 38 | MOV {T(0)} {local(a, 0)} 39 | BR {L2} 40 | 41 | 42 | 43 | 44 | L2->L3 45 | 46 | 47 | 48 | 49 | 50 | L4 51 | L4 52 | BR {L1} 53 | 54 | 55 | 56 | 57 | L2->L4 58 | 59 | 60 | 61 | 62 | 63 | L3->L2 64 | 65 | 66 | 67 | 68 | 69 | L1 70 | 71 | L1 72 | 73 | 74 | 75 | L4->L1 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/cfg/if1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | BR {L2} 17 | 18 | 19 | 20 | 21 | L2 22 | L2 23 | LIii {1 Kint(0), 2 Kint(1)} {T(0)} 24 | CBR {T(0)} {L3, L4} 25 | 26 | 27 | 28 | 29 | L0->L2 30 | 31 | 32 | 33 | 34 | 35 | L3 36 | L3 37 | LOADGLOBAL {print} {T(1)} 38 | CALL {T(1), 'hi' Ks(2)} {T(1..)} 39 | BR {L4} 40 | 41 | 42 | 43 | 44 | L2->L3 45 | 46 | 47 | 48 | 49 | 50 | L4 51 | L4 52 | BR {L1} 53 | 54 | 55 | 56 | 57 | L2->L4 58 | 59 | 60 | 61 | 62 | 63 | L3->L4 64 | 65 | 66 | 67 | 68 | 69 | L1 70 | 71 | L1 72 | 73 | 74 | 75 | L4->L1 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/gaussian2_lib.lua: -------------------------------------------------------------------------------- 1 | -- Gaussian elimination 2 | local assert = assert 3 | local slice, numarray, intarray = table.slice, table.numarray, table.intarray 4 | local write = io.write 5 | 6 | local function copy(a: number[]) 7 | local c: number[] = numarray(#a, 0.0) 8 | for i = 1,#a do 9 | c[i] = a[i] 10 | end 11 | return c 12 | end 13 | 14 | -- i = column 15 | local function partial_pivot(columns: table, nrow: integer[], i: integer, n: integer) 16 | local p: integer = i 17 | local max: number = 0.0 18 | local a: number[] = @number[]( columns[i] ) 19 | local max_set = false 20 | 21 | -- find the row from i to n that has 22 | -- max absolute value in column[i] 23 | for row=i, n do 24 | local value: number = a[nrow[row]] 25 | if value < 0.0 then value = -value end 26 | if not max_set then 27 | max = value 28 | max_set = true 29 | p = row 30 | elseif value > max then 31 | p = row 32 | max = value 33 | end 34 | end 35 | if a[p] == 0.0 then 36 | error("no unique solution exists") 37 | end 38 | if nrow[i] ~= nrow[p] then 39 | write('Performing row interchange ', i, ' will be swapped with ', p, "\\n") 40 | local temp: integer = nrow[i] 41 | nrow[i] = nrow[p] 42 | nrow[p] = temp 43 | end 44 | end 45 | 46 | local function dump_matrix(columns: table, m: integer, n: integer, nrow: integer[]) 47 | for i = 1,m do 48 | for j = 1,n do 49 | write(columns[j][nrow[i]], ' ') 50 | end 51 | write("\\n") 52 | end 53 | end 54 | 55 | local function gaussian_solve(A: number[], b: number[], m: integer, n: integer) 56 | 57 | -- make copies 58 | A = copy(A) 59 | b = copy(b) 60 | 61 | assert(m == n) 62 | assert(#b == m) 63 | 64 | -- nrow will hold the order of the rows allowing 65 | -- easy interchange of rows 66 | local nrow: integer[] = intarray(n) 67 | 68 | -- As ravi matrices are column major we 69 | -- create slices for each column for easy access 70 | -- the vector b can also be treated as an additional 71 | -- column thereby creating the augmented matrix 72 | local columns: table = {} 73 | 74 | -- we use i as the row and j a the column 75 | 76 | -- first get the column slices 77 | for j = 1,n do 78 | columns[j] = slice(A, (j-1)*m+1, m) 79 | end 80 | columns[n+1] = b 81 | 82 | -- initialize the nrow vector 83 | for i = 1,n do 84 | nrow[i] = i 85 | end 86 | 87 | for j = 1,n-1 do -- j is the column 88 | partial_pivot(columns, nrow, j, m) 89 | 90 | dump_matrix(columns, n, n+1, nrow) 91 | 92 | for i = j+1,m do -- i is the row 93 | -- obtain the column j 94 | local column: number[] = @number[]( columns[j] ) 95 | local multiplier: number = column[nrow[i]]/column[nrow[j]] 96 | write('m(' .. i .. ',' .. j .. ') = ', column[nrow[i]], ' / ', column[nrow[j]], "\\n") 97 | write('Performing R(' .. i .. ') = R(' .. i .. ') - m(' .. i .. ',' .. j .. ') * R(' .. j .. ')\\n') 98 | -- For the row i, we need to 99 | -- do row(i) = row(i) - multipler * row(j) 100 | for q = j,n+1 do 101 | local col: number[] = @number[]( columns[q] ) 102 | col[nrow[i]] = col[nrow[i]] - multiplier*col[nrow[j]] 103 | end 104 | end 105 | 106 | write("Post elimination column ", j, "\\n") 107 | dump_matrix(columns, n, n+1, nrow) 108 | end 109 | 110 | if columns[n][nrow[n]] == 0.0 then 111 | error("no unique solution exists") 112 | end 113 | 114 | 115 | -- Now we do the back substitution 116 | local x: number[] = numarray(n, 0.0) 117 | local a: number[] = @number[]( columns[n] ) 118 | 119 | write('Performing back substitution\\n') 120 | x[n] = b[nrow[n]] / a[nrow[n]] 121 | write('x[', n, '] = b[', n, '] / a[', n, '] = ', x[n], "\\n") 122 | for i = n-1,1,-1 do 123 | local sum: number 124 | for j = i+1, n do 125 | a = @number[]( columns[j] ) 126 | sum = sum + a[nrow[i]] * x[j] 127 | if j == i+1 then 128 | write('sum = ') 129 | else 130 | write('sum = sum + ') 131 | end 132 | write('a[', i, ', ', j, '] * x[', j, ']', "\\n") 133 | end 134 | write('sum = ', sum, '\\n') 135 | a = @number[]( columns[i] ) 136 | x[i] = (b[nrow[i]] - sum) / a[nrow[i]] 137 | write('x[',i,'] = (b[', i, '] - sum) / a[', i, ', ', i, '] = ', x[i], "\\n") 138 | end 139 | 140 | return x 141 | end 142 | 143 | return { 144 | gaussian_solve=gaussian_solve 145 | } 146 | -------------------------------------------------------------------------------- /src/ravi_binding.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | /* This will contain Lua bindings */ 25 | 26 | #include 27 | #include 28 | 29 | #include "cfg.h" 30 | #include "codegen.h" 31 | #include "optimizer.h" 32 | 33 | int raviX_compile(struct Ravi_CompilerInterface *compiler_interface) 34 | { 35 | int rc = 0; 36 | int dump_ir = 0; 37 | int dump_ast = 0; 38 | if (compiler_interface->compiler_options != NULL) { 39 | dump_ir = strstr(compiler_interface->compiler_options, "--dump-ir") != NULL; 40 | dump_ast = strstr(compiler_interface->compiler_options, "--dump-ast") != NULL; 41 | } 42 | compiler_interface->generated_code = NULL; 43 | CompilerState *compiler_state = raviX_init_compiler(compiler_interface->memory_allocator); 44 | LinearizerState *linearizer = raviX_init_linearizer(compiler_state); 45 | rc = raviX_parse(compiler_state, compiler_interface->source, compiler_interface->source_len, 46 | compiler_interface->source_name); 47 | if (rc != 0) { 48 | compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(compiler_state)); 49 | goto L_exit; 50 | } 51 | if (dump_ast) { 52 | TextBuffer mbuf; 53 | raviX_buffer_init(&mbuf, 1024); 54 | raviX_dump_ast_to_buffer(compiler_state, &mbuf); 55 | compiler_interface->generated_code = mbuf.buf; 56 | goto L_exit; 57 | } 58 | rc = raviX_ast_lower(compiler_state); 59 | if (rc != 0) { 60 | compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(compiler_state)); 61 | goto L_exit; 62 | } 63 | rc = raviX_ast_typecheck(compiler_state); 64 | if (rc != 0) { 65 | compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(compiler_state)); 66 | goto L_exit; 67 | } 68 | rc = raviX_ast_simplify(compiler_state); 69 | if (rc != 0) { 70 | compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(compiler_state)); 71 | goto L_exit; 72 | } 73 | rc = raviX_ast_linearize(linearizer); 74 | if (rc != 0) { 75 | compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(compiler_state)); 76 | goto L_exit; 77 | } 78 | raviX_construct_cfg(linearizer->main_proc); 79 | raviX_remove_unreachable_blocks(linearizer); 80 | raviX_optimize_upvalues(linearizer); 81 | 82 | TextBuffer buf; 83 | raviX_buffer_init(&buf, 4096); 84 | if (dump_ir) { 85 | raviX_buffer_add_string(&buf, "#if 0\n"); 86 | raviX_buffer_add_string(&buf, "// Following is an IR Dump from the compiler\n"); 87 | raviX_show_linearizer(linearizer, &buf); 88 | raviX_buffer_add_string(&buf, "\n// End of IR dump\n"); 89 | raviX_buffer_add_string(&buf, "#endif\n"); 90 | } 91 | rc = raviX_generate_C(linearizer, &buf, compiler_interface); 92 | compiler_interface->generated_code = buf.buf; 93 | 94 | L_exit: 95 | raviX_destroy_linearizer(linearizer); 96 | raviX_destroy_compiler(compiler_state); 97 | 98 | return rc; 99 | } 100 | 101 | void raviX_release(struct Ravi_CompilerInterface *compiler_interface) 102 | { 103 | if (compiler_interface->generated_code != NULL) { 104 | raviX_free((void *)compiler_interface->generated_code); 105 | compiler_interface->generated_code = NULL; 106 | } 107 | } -------------------------------------------------------------------------------- /docs/cfg/for3.gv: -------------------------------------------------------------------------------- 1 | digraph Proc1 { 2 | L0 [shape=none, margin=0, label=< 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
L0
MOV {1 Kint(0)} {Tint(1)}
MOV {10 Kint(1)} {Tint(2)}
MOV {1 Kint(0)} {Tint(3)}
LIii {0 Kint(2), Tint(3)} {Tint(4)}
SUBii {Tint(1), Tint(3)} {Tint(1)}
BR {L2}
>]; 11 | L0 -> L2 12 | L2 [shape=none, margin=0, label=< 13 | 14 | 15 | 16 |
L2
ADDii {Tint(1), Tint(3)} {Tint(1)}
CBR {Tint(4)} {L3, L4}
>]; 17 | L2 -> L3 18 | L2 -> L4 19 | L3 [shape=none, margin=0, label=< 20 | 21 | 22 | 23 |
L3
LEii {Tint(2), Tint(1)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 24 | L3 -> L6 25 | L3 -> L5 26 | L4 [shape=none, margin=0, label=< 27 | 28 | 29 | 30 |
L4
LIii {Tint(1), Tint(2)} {Tint(5)}
CBR {Tint(5)} {L6, L5}
>]; 31 | L4 -> L6 32 | L4 -> L5 33 | L5 [shape=none, margin=0, label=< 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
L5
MOV {Tint(1)} {Tint(0)}
MOV {10 Kint(1)} {Tint(7)}
MOV {1 Kint(0)} {Tint(8)}
UNMi {1 Kint(0)} {Tint(9)}
MOV {Tint(9)} {Tint(10)}
LIii {0 Kint(2), Tint(10)} {Tint(11)}
SUBii {Tint(7), Tint(10)} {Tint(7)}
BR {L7}
>]; 44 | L5 -> L7 45 | L6 [shape=none, margin=0, label=< 46 | 47 | 48 |
L6
BR {L1}
>]; 49 | L6 -> L1 50 | L7 [shape=none, margin=0, label=< 51 | 52 | 53 | 54 |
L7
ADDii {Tint(7), Tint(10)} {Tint(7)}
CBR {Tint(11)} {L8, L9}
>]; 55 | L7 -> L8 56 | L7 -> L9 57 | L8 [shape=none, margin=0, label=< 58 | 59 | 60 | 61 |
L8
LEii {Tint(8), Tint(7)} {Tint(12)}
CBR {Tint(12)} {L11, L10}
>]; 62 | L8 -> L11 63 | L8 -> L10 64 | L9 [shape=none, margin=0, label=< 65 | 66 | 67 | 68 |
L9
LIii {Tint(7), Tint(8)} {Tint(12)}
CBR {Tint(12)} {L11, L10}
>]; 69 | L9 -> L11 70 | L9 -> L10 71 | L10 [shape=none, margin=0, label=< 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
L10
MOV {Tint(7)} {Tint(6)}
MOV {1 Kint(0)} {Tint(14)}
MOV {3 Kint(3)} {Tint(15)}
MOV {2 Kint(4)} {Tint(16)}
LIii {0 Kint(2), Tint(16)} {Tint(17)}
SUBii {Tint(14), Tint(16)} {Tint(14)}
BR {L12}
>]; 81 | L10 -> L12 82 | L11 [shape=none, margin=0, label=< 83 | 84 | 85 |
L11
BR {L2}
>]; 86 | L11 -> L2 87 | L12 [shape=none, margin=0, label=< 88 | 89 | 90 | 91 |
L12
ADDii {Tint(14), Tint(16)} {Tint(14)}
CBR {Tint(17)} {L13, L14}
>]; 92 | L12 -> L13 93 | L12 -> L14 94 | L13 [shape=none, margin=0, label=< 95 | 96 | 97 | 98 |
L13
LEii {Tint(15), Tint(14)} {Tint(18)}
CBR {Tint(18)} {L16, L15}
>]; 99 | L13 -> L16 100 | L13 -> L15 101 | L14 [shape=none, margin=0, label=< 102 | 103 | 104 | 105 |
L14
LIii {Tint(14), Tint(15)} {Tint(18)}
CBR {Tint(18)} {L16, L15}
>]; 106 | L14 -> L16 107 | L14 -> L15 108 | L15 [shape=none, margin=0, label=< 109 | 110 | 111 | 112 | 113 | 114 |
L15
MOV {Tint(14)} {Tint(13)}
LOADGLOBAL {print} {T(0)}
CALL {T(0), Tint(0), Tint(6), Tint(13)} {T(0..)}
BR {L12}
>]; 115 | L15 -> L12 116 | L16 [shape=none, margin=0, label=< 117 | 118 | 119 |
L16
BR {L7}
>]; 120 | L16 -> L7 121 | } 122 | -------------------------------------------------------------------------------- /src/dataflow_framework.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | /** 25 | * A framework for performing data flow analysis. 26 | * The framework is based upon similar framework in MIR project (https://github.com/vnmakarov/mir) 27 | */ 28 | 29 | #include "dataflow_framework.h" 30 | #include "allocate.h" 31 | #include "graph.h" 32 | #include "bitset.h" 33 | 34 | #include 35 | 36 | DECLARE_ARRAY(GraphNodeArray, GraphNode *); 37 | 38 | struct dataflow_context { 39 | Graph *g; 40 | GraphNodeArray worklist; 41 | GraphNodeArray pending; 42 | BitSet bb_to_consider; 43 | void *userdata; 44 | }; 45 | 46 | static void init_data_flow(struct dataflow_context *dataflow_context, Graph *g) 47 | { 48 | memset(dataflow_context, 0, sizeof *dataflow_context); 49 | raviX_bitset_create2(&dataflow_context->bb_to_consider, 512); 50 | dataflow_context->g = g; 51 | } 52 | 53 | static void finish_data_flow(struct dataflow_context *dataflow_context) 54 | { 55 | array_clearmem(&dataflow_context->worklist); 56 | array_clearmem(&dataflow_context->pending); 57 | raviX_bitset_destroy(&dataflow_context->bb_to_consider); 58 | } 59 | 60 | void raviX_solve_dataflow(Graph *g, bool forward_p, 61 | int (*join_function)(void *, nodeId_t, bool), 62 | int (*transfer_function)(void *, nodeId_t), void *userdata) 63 | { 64 | unsigned iter; 65 | struct dataflow_context ctx; 66 | GraphNodeArray *worklist; 67 | GraphNodeArray *pending; 68 | 69 | init_data_flow(&ctx, g); 70 | worklist = &ctx.worklist; 71 | pending = &ctx.pending; 72 | 73 | /* ensure that the graph has RPO calculated */ 74 | raviX_classify_edges(ctx.g); 75 | 76 | worklist->count = 0; 77 | /* Initially the basic blocks are added to the worklist */ 78 | for (uint32_t i = 0; i < raviX_graph_size(ctx.g); i++) { 79 | array_push(worklist, GraphNode *, raviX_graph_node(ctx.g, i)); 80 | } 81 | iter = 0; 82 | while (worklist->count != 0) { 83 | GraphNode **addr = worklist->data; 84 | raviX_sort_nodes_by_RPO(addr, worklist->count, forward_p); 85 | raviX_bitset_clear(&ctx.bb_to_consider); 86 | pending->count = 0; 87 | for (unsigned i = 0; i < worklist->count; i++) { 88 | int changed_p = iter == 0; 89 | GraphNode *bb = addr[i]; 90 | GraphNodeList *nodes = forward_p ? raviX_predecessors(bb) : raviX_successors(bb); 91 | // TODO should we pass the nodes array to the join function? 92 | if (raviX_node_list_size(nodes) == 0) 93 | join_function(ctx.userdata, raviX_node_index(bb), true); 94 | else 95 | changed_p |= join_function(ctx.userdata, raviX_node_index(bb), false); 96 | if (changed_p && transfer_function(ctx.userdata, raviX_node_index(bb))) { 97 | GraphNodeList *list = forward_p ? raviX_successors(bb) : raviX_predecessors(bb); 98 | for (unsigned i = 0; i < raviX_node_list_size(list); i++) { 99 | nodeId_t index = raviX_node_list_at(list, i); 100 | /* If this bb is not already been added to pending then add it */ 101 | if (raviX_bitset_set_bit_p(&ctx.bb_to_consider, index)) { 102 | array_push(pending, GraphNode *, raviX_graph_node(ctx.g, index)); 103 | } 104 | } 105 | } 106 | } 107 | iter++; 108 | { 109 | /* Swap worklist and pending */ 110 | GraphNodeArray *t = worklist; 111 | worklist = pending; 112 | pending = t; 113 | } 114 | } 115 | 116 | finish_data_flow(&ctx); 117 | } 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Dibyendu Majumdar 4 | Portions Copyright (c) 1994–2019 Lua.org, PUC-Rio. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | 25 | Third-party licences: 26 | 27 | a) https://github.com/ulfjack/ryu 28 | Copyright 2018 Ulf Adams 29 | 30 | The contents of this file may be used under the terms of the Apache License, 31 | Version 2.0. 32 | 33 | (See accompanying file LICENSE-Apache or copy at 34 | http://www.apache.org/licenses/LICENSE-2.0) 35 | 36 | Alternatively, the contents of this file may be used under the terms of 37 | the Boost Software License, Version 1.0. 38 | (See accompanying file LICENSE-Boost or copy at 39 | https://www.boost.org/LICENSE_1_0.txt) 40 | 41 | Unless required by applicable law or agreed to in writing, this software 42 | is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43 | KIND, either express or implied. 44 | 45 | b) https://github.com/anholt/hash_table 46 | 47 | Copyright © 1988-2004 Keith Packard and Bart Massey. All Rights Reserved. 48 | Copyright © 2009 Intel Corporation 49 | 50 | Permission is hereby granted, free of charge, to any person 51 | obtaining a copy of this software and associated 52 | documentation files (the "Software"), to deal in the 53 | Software without restriction, including without limitation 54 | the rights to use, copy, modify, merge, publish, distribute, 55 | sublicense, and/or sell copies of the Software, and to 56 | permit persons to whom the Software is furnished to do so, 57 | subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall 60 | be included in all copies or substantial portions of the 61 | Software. 62 | 63 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 64 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 65 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 66 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 67 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 68 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 70 | OTHER DEALINGS IN THE SOFTWARE. 71 | 72 | Except as contained in this notice, the names of the authors 73 | or their institutions shall not be used in advertising or 74 | otherwise to promote the sale, use or other dealings in this 75 | Software without prior written authorization from the 76 | authors. 77 | 78 | d) https://github.com/rui314/chibicc 79 | 80 | MIT License 81 | 82 | Copyright (c) 2019 Rui Ueyama 83 | 84 | Permission is hereby granted, free of charge, to any person obtaining a copy 85 | of this software and associated documentation files (the "Software"), to deal 86 | in the Software without restriction, including without limitation the rights 87 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 88 | copies of the Software, and to permit persons to whom the Software is 89 | furnished to do so, subject to the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be included in all 92 | copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 95 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 96 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 97 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 98 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 99 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 100 | SOFTWARE. 101 | 102 | -------------------------------------------------------------------------------- /src/df_liveness.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | /* 25 | * Calculate variable liveness 26 | * This will use the Dataflow Framework. 27 | * Implementation inspired by one in MIR 28 | */ 29 | 30 | 31 | #include "bitset.h" 32 | #include "dataflow_framework.h" 33 | #include "linearizer.h" 34 | #include "allocate.h" 35 | 36 | struct liveness_info { 37 | nodeId_t node_id; 38 | BitSet in; 39 | BitSet out; 40 | BitSet use; 41 | BitSet def; 42 | }; 43 | 44 | DECLARE_ARRAY(liveness_info_array, struct liveness_info *); 45 | 46 | struct liveness_data { 47 | Proc *proc; 48 | struct liveness_info_array lives; 49 | }; 50 | 51 | static void init_liveness_data(Proc *proc, struct liveness_data *liveness_data) 52 | { 53 | memset(liveness_data, 0, sizeof(*liveness_data)); 54 | for (unsigned i = 0; i < proc->node_count; i++) { 55 | struct liveness_info *liveness_info = (struct liveness_info *)raviX_calloc(1, sizeof(struct liveness_info)); 56 | liveness_info->node_id = i; 57 | raviX_bitset_create(&liveness_info->use); 58 | raviX_bitset_create(&liveness_info->def); 59 | raviX_bitset_create(&liveness_info->in); 60 | raviX_bitset_create(&liveness_info->out); 61 | array_push(&liveness_data->lives, struct liveness_info *, liveness_info); 62 | } 63 | } 64 | 65 | static void destroy_liveness_data(struct liveness_data *liveness_data) 66 | { 67 | for (unsigned i = 0; i < liveness_data->lives.count; i++) { 68 | raviX_bitset_create(&liveness_data->lives.data[i]->use); 69 | raviX_bitset_create(&liveness_data->lives.data[i]->def); 70 | raviX_bitset_create(&liveness_data->lives.data[i]->in); 71 | raviX_bitset_create(&liveness_data->lives.data[i]->out); 72 | } 73 | array_clearmem(&liveness_data->lives); 74 | } 75 | 76 | static inline struct liveness_info *get_liveness_info(struct liveness_data *liveness_data, nodeId_t id) 77 | { 78 | return liveness_data->lives.data[id]; 79 | } 80 | 81 | /* Life analysis */ 82 | static int live_join_func(void *userdata, nodeId_t id, bool init) 83 | { 84 | struct liveness_data *liveness_data = (struct liveness_data *)userdata; 85 | struct liveness_info *liveness_info = get_liveness_info(liveness_data, id); 86 | if (init) { 87 | raviX_bitset_clear(&liveness_info->in); 88 | return 0; 89 | } else { 90 | GraphNodeList *successors = raviX_successors(raviX_graph_node(liveness_data->proc->cfg, id)); 91 | int changed = 0; 92 | // out[n] = Union of in[s] where s in succ[n] 93 | for (unsigned i = 0; i < raviX_node_list_size(successors); i++) { 94 | nodeId_t succ_id = raviX_node_list_at(successors, i); 95 | struct liveness_info *successor_liveness_info = get_liveness_info(liveness_data, succ_id); 96 | changed |= 97 | raviX_bitset_ior(&liveness_info->out, &liveness_info->out, &successor_liveness_info->in); 98 | } 99 | return changed; 100 | } 101 | } 102 | 103 | static int live_transfer_func(void *userdata, nodeId_t id) 104 | { 105 | struct liveness_data *liveness_data = (struct liveness_data *)userdata; 106 | struct liveness_info *liveness_info = get_liveness_info(liveness_data, id); 107 | // out[n] = use[n] U (out[n] - def[n]) 108 | // In bitset terms out[n] = use[n] | (out[n] & ~def[n]) 109 | return raviX_bitset_ior_and_compl(&liveness_info->in, &liveness_info->use, &liveness_info->out, 110 | &liveness_info->def); 111 | } 112 | 113 | // TODO 114 | 115 | // Compute use/def sets of each node 116 | // If a reg appears as the target of an instruction that's a def 117 | // If a reg is used as operand then its a use 118 | // Need to handle ranges / var args too 119 | // Or should we restrict analysis to certain types of regs? 120 | 121 | // Right now we have disjoint sets for temps / locals - to do this efficiently we need a merged set of regs for each proc 122 | // Liveness analysis is a backward data flow problem 123 | // see calculate_func_cfg_live_info in mir_genc.c 124 | -------------------------------------------------------------------------------- /docs/cfg/if2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | BR {L2} 17 | 18 | 19 | 20 | 21 | L2 22 | L2 23 | LIii {1 Kint(0), 2 Kint(1)} {T(0)} 24 | CBR {T(0)} {L3, L4} 25 | 26 | 27 | 28 | 29 | L0->L2 30 | 31 | 32 | 33 | 34 | 35 | L3 36 | L3 37 | LOADGLOBAL {print} {T(1)} 38 | CALL {T(1), 'hi' Ks(2)} {T(1..)} 39 | BR {L5} 40 | 41 | 42 | 43 | 44 | L2->L3 45 | 46 | 47 | 48 | 49 | 50 | L4 51 | L4 52 | LOADGLOBAL {print} {T(2)} 53 | CALL {T(2), 'no' Ks(3)} {T(2..)} 54 | BR {L5} 55 | 56 | 57 | 58 | 59 | L2->L4 60 | 61 | 62 | 63 | 64 | 65 | L5 66 | L5 67 | BR {L1} 68 | 69 | 70 | 71 | 72 | L3->L5 73 | 74 | 75 | 76 | 77 | 78 | L4->L5 79 | 80 | 81 | 82 | 83 | 84 | L1 85 | 86 | L1 87 | 88 | 89 | 90 | L5->L1 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/graph.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef ravicomp_GRAPH_H 25 | #define ravicomp_GRAPH_H 26 | 27 | #include "allocate.h" 28 | #include "common.h" 29 | 30 | #include 31 | #include 32 | 33 | /* 34 | * Various graph manipulation routines. 35 | * The graph is designed to manage nodes that are just integer ids. 36 | * Node ids range from [0..n) - hence one can simply represent nodes as arrays. 37 | * 38 | * The graph structure does not care what the node represents and 39 | * knows nothing about it. The benefit of this approach is that we can make 40 | * the graph algorithms reusable. There may be some performance cost as we 41 | * need to map node ids to nodes. 42 | * 43 | * The assumption here is that each node corresponds to a basic block in 44 | * the program intermediate code. And each basic block is identified by a node 45 | * id which can be used to construct the control flow graph. 46 | */ 47 | 48 | /* nodeId_t is declared elsewhere */ 49 | #ifndef RAVIX_GRAPH_DEFINED 50 | #define RAVIX_GRAPH_DEFINED 51 | typedef struct Graph Graph; 52 | #endif 53 | typedef struct GraphNode GraphNode; 54 | typedef struct GraphNodeList GraphNodeList; 55 | enum EdgeType { 56 | EDGE_TYPE_UNCLASSIFIED = 0, 57 | EDGE_TYPE_TREE = 1, 58 | EDGE_TYPE_BACKWARD = 2, 59 | EDGE_TYPE_FORWARD = 4, 60 | EDGE_TYPE_CROSS = 8 61 | }; 62 | 63 | 64 | /* Initialize the graph data structure and associate some userdata with it. */ 65 | Graph *raviX_init_graph(nodeId_t entry, nodeId_t exit, void *userdata, C_MemoryAllocator *allocator); 66 | /* Destroy the graph data structure */ 67 | void raviX_destroy_graph(Graph *g); 68 | 69 | /* Add an edge from one node a to b. Both nodes a and b will be implicitly added 70 | * to the graph if they do not already exist. 71 | */ 72 | void raviX_add_edge(Graph *g, nodeId_t a, nodeId_t b); 73 | /* Check if an edge exists from one node a to b */ 74 | bool raviX_has_edge(Graph *g, nodeId_t a, nodeId_t b); 75 | /* Delete an edge from a to b */ 76 | void raviX_delete_edge(Graph *g, nodeId_t a, nodeId_t b); 77 | /* Get the edge classification for edge from a to b; this is only available if graph has been 78 | * analyzed for edges. */ 79 | enum EdgeType raviX_get_edge_type(Graph *g, nodeId_t a, nodeId_t b); 80 | 81 | /* Get node identified by index */ 82 | GraphNode *raviX_graph_node(Graph *g, nodeId_t index); 83 | /* Get the RPO - reverse post order index of the node */ 84 | uint32_t raviX_node_RPO(GraphNode *n); 85 | /* Get the node's id */ 86 | nodeId_t raviX_node_index(GraphNode *n); 87 | /* Get list of predecessors */ 88 | GraphNodeList *raviX_predecessors(GraphNode *n); 89 | /* Get list of successors */ 90 | GraphNodeList *raviX_successors(GraphNode *n); 91 | 92 | /* Number of entries in the node_list */ 93 | uint32_t raviX_node_list_size(GraphNodeList *list); 94 | /* Get the nodeId at given node_link position */ 95 | nodeId_t raviX_node_list_at(GraphNodeList *list, uint32_t i); 96 | 97 | void raviX_for_each_node(Graph *g, void (*callback)(void *arg, Graph *g, nodeId_t nodeid), void *arg); 98 | 99 | /* 100 | * Classifies links in the graph and also computes the 101 | * reverse post order value. 102 | */ 103 | void raviX_classify_edges(Graph *g); 104 | /* 105 | * Returns a sorted array (allocated). 106 | * Sorted by reverse postorder value. 107 | * If forward=true then 108 | * it will be the opposite direction, so to get reverse postorder, 109 | * set forward=false. 110 | * You must deallocate the array when done. 111 | * The array size will be equal to raviX_graph_size(g). 112 | * Before attempting to sort, you must have called 113 | * raviX_classify_edges(g). 114 | */ 115 | GraphNode **raviX_graph_nodes_sorted_by_RPO(Graph *g, bool forward); 116 | 117 | void raviX_sort_nodes_by_RPO(GraphNode **nodes, size_t count, bool forward); 118 | 119 | /* says how many nodes are in the graph */ 120 | uint32_t raviX_graph_size(Graph *g); 121 | /* Generates GraphViz (dot) output */ 122 | void raviX_draw_graph(Graph *g, FILE *fp); 123 | 124 | 125 | #endif -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(RaviCompiler VERSION 0.0.1 LANGUAGES C) 3 | 4 | option(ASAN "Controls whether address sanitizer should be enabled" OFF) 5 | 6 | set(PUBLIC_HEADERS 7 | include/ravi_compiler.h 8 | include/ravi_api.h) 9 | 10 | set(HEADERS 11 | ${PUBLIC_HEADERS} 12 | src/allocate.h 13 | src/bitset.h 14 | src/ptrlist.h 15 | src/fnv_hash.h 16 | src/graph.h 17 | src/hash_table.h 18 | src/set.h 19 | src/membuf.h 20 | src/cfg.h 21 | src/dominator.h 22 | src/linearizer.h 23 | src/common.h 24 | src/dataflow_framework.h 25 | src/optimizer.h 26 | src/parser.h 27 | src/codegen.h 28 | src/chibicc/chibicc.h) 29 | 30 | set(SRCS 31 | src/allocate.c 32 | src/ast_walker.c 33 | src/ast_simplify.c 34 | src/ast_lower.c 35 | src/bitset.c 36 | src/ptrlist.c 37 | src/fnv_hash.c 38 | src/graph.c 39 | src/cfg.c 40 | src/dominator.c 41 | src/hash_table.c 42 | src/set.c 43 | src/lexer.c 44 | src/parser.c 45 | src/ast_printer.c 46 | src/ast_printer_n.c 47 | src/typechecker.c 48 | src/linearizer.c 49 | src/dataflow_framework.c 50 | src/opt_unusedcode.c 51 | src/membuf.c 52 | src/df_liveness.c 53 | src/codegen.c 54 | src/ravi_binding.c 55 | src/chibicc/chibicc_tokenize.c 56 | src/chibicc/chibicc_parse.c 57 | src/chibicc/chibicc_type.c 58 | src/chibicc/chibicc_strings.c 59 | src/chibicc/chibicc_unicode.c 60 | src/chibicc/chibicc_hashmap.c 61 | ) 62 | 63 | message("SOURCE dir is ${RaviCompiler_SOURCE_DIR}") 64 | 65 | if ($ENV{CLION_IDE}) 66 | # CLion seems unable to handle include paths set on sources 67 | include_directories("${RaviCompiler_SOURCE_DIR}/include") 68 | endif () 69 | 70 | if (WIN32) 71 | # disable warnings about C string functions 72 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS) 73 | endif() 74 | 75 | include(CheckCCompilerFlag) 76 | if (NOT MSVC) 77 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wmissing-prototypes -Wstrict-prototypes -Werror=return-type") 78 | if (ASAN) 79 | set(CMAKE_REQUIRED_FLAGS "-fsanitize=address") 80 | check_c_compiler_flag("-fsanitize=address" COMPILER_ASAN_SUPPORTED) 81 | if (COMPILER_ASAN_SUPPORTED AND NOT CMAKE_C_FLAGS_DEBUG MATCHES "-fsanitize=address") 82 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address") 83 | endif () 84 | endif () 85 | endif() 86 | 87 | include(GNUInstallDirs) 88 | 89 | set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) 90 | 91 | if (NOT WIN32) 92 | set(EXTRA_LIBRARIES m) 93 | endif () 94 | 95 | if (WIN32) 96 | set(LIBTYPE STATIC) 97 | else() 98 | set(LIBTYPE SHARED) 99 | endif() 100 | add_library(ravicomp ${LIBTYPE} 101 | ${HEADERS} 102 | ${SRCS}) 103 | target_include_directories(ravicomp 104 | PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" 105 | PUBLIC "${RaviCompiler_SOURCE_DIR}/include" 106 | PRIVATE "${RaviCompiler_SOURCE_DIR}/src") 107 | target_link_libraries(ravicomp ${EXTRA_LIBRARIES}) 108 | include(GenerateExportHeader) 109 | generate_export_header(ravicomp) 110 | 111 | add_executable(tmisc tests/tmisc.c tests/ravi_alloc.c) 112 | target_link_libraries(tmisc ravicomp) 113 | target_include_directories(tmisc 114 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" 115 | PRIVATE "${RaviCompiler_SOURCE_DIR}/src" 116 | PRIVATE "${RaviCompiler_SOURCE_DIR}/include") 117 | 118 | add_executable(tastwalk tests/tastwalk.c tests/ravi_alloc.c tests/tcommon.c tests/tcommon.h) 119 | target_link_libraries(tastwalk ravicomp) 120 | target_include_directories(tastwalk 121 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" 122 | PRIVATE "${RaviCompiler_SOURCE_DIR}/include") 123 | 124 | add_executable(trun tests/trun.c tests/tcommon.c tests/ravi_alloc.c tests/tcommon.h) 125 | target_link_libraries(trun ravicomp) 126 | target_include_directories(trun 127 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" 128 | PRIVATE "${RaviCompiler_SOURCE_DIR}/src" 129 | PRIVATE "${RaviCompiler_SOURCE_DIR}/include") 130 | 131 | add_executable(tgraph tests/tgraph.c tests/ravi_alloc.c) 132 | target_link_libraries(tgraph ravicomp) 133 | target_include_directories(tgraph 134 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" 135 | PRIVATE "${RaviCompiler_SOURCE_DIR}/src" 136 | PRIVATE "${RaviCompiler_SOURCE_DIR}/include") 137 | 138 | add_executable(tchibicc tests/tchibicc.c tests/ravi_alloc.c) 139 | target_link_libraries(tchibicc ravicomp) 140 | target_include_directories(tchibicc 141 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" 142 | PRIVATE "${RaviCompiler_SOURCE_DIR}/src" 143 | PRIVATE "${RaviCompiler_SOURCE_DIR}/include") 144 | 145 | install(FILES ${PUBLIC_HEADERS} 146 | DESTINATION include/ravicomp) 147 | install(TARGETS ravicomp 148 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT RaviCompiler_Runtime 149 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Development 150 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Runtime) 151 | install(FILES 152 | ${PROJECT_BINARY_DIR}/ravicomp_export.h DESTINATION include/ravicomp 153 | ) -------------------------------------------------------------------------------- /tests/tcommon.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include "tcommon.h" 25 | #include "ravi_alloc.h" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | void parse_arguments(struct arguments *args, int argc, const char *argv[]) 32 | { 33 | memset(args, 0, sizeof *args); 34 | args->typecheck = 1; 35 | args->linearize = 1; 36 | args->astdump = 1; 37 | args->irdump = 1; 38 | args->cfgdump = 1; 39 | args->codump = 1; 40 | args->table_ast = 0; 41 | args->simplify_ast = 0; 42 | args->remove_unreachable_blocks = 0; 43 | args->gen_C = 0; 44 | args->opt_upvalue = 0; 45 | args->mainfunc = "setup"; 46 | for (int i = 1; i < argc; i++) { 47 | if (strcmp(argv[i], "--notypecheck") == 0) { 48 | args->typecheck = 0; 49 | } else if (strcmp(argv[i], "--nolinearize") == 0) { 50 | args->linearize = 0; 51 | } else if (strcmp(argv[i], "--noastdump") == 0) { 52 | args->astdump = 0; 53 | } else if (strcmp(argv[i], "--noirdump") == 0) { 54 | args->irdump = 0; 55 | } else if (strcmp(argv[i], "--nocodump") == 0) { 56 | args->codump = 0; 57 | } else if (strcmp(argv[i], "--nocfgdump") == 0) { 58 | args->cfgdump = 0; 59 | } else if (strcmp(argv[i], "--simplify-ast") == 0) { 60 | args->simplify_ast = 1; 61 | } else if (strcmp(argv[i], "--remove-unreachable-blocks") == 0) { 62 | args->remove_unreachable_blocks = 1; 63 | } else if (strcmp(argv[i], "--gen-C") == 0) { 64 | args->gen_C = 1; 65 | } else if (strcmp(argv[i], "--opt-upvalues") == 0) { 66 | args->opt_upvalue = 1; 67 | } else if (strcmp(argv[i], "--table-ast") == 0) { 68 | args->table_ast = 1; 69 | } else if (strcmp(argv[i], "-main") == 0) { 70 | if (i < argc - 1) { 71 | i++; 72 | args->mainfunc = strdup(argv[i]); 73 | } else { 74 | fprintf(stderr, "Missing argument after -main\n"); 75 | exit(1); 76 | } 77 | } else if (strcmp(argv[i], "-f") == 0) { 78 | if (args->filename) { 79 | fprintf(stderr, "-f already accepted\n"); 80 | continue; 81 | } 82 | if (i < argc - 1) { 83 | i++; 84 | args->filename = strdup(argv[i]); 85 | } else { 86 | fprintf(stderr, "Missing file name after -f\n"); 87 | exit(1); 88 | } 89 | } else { 90 | if (args->code) { 91 | fprintf(stderr, "Bad argument at %d", i); 92 | exit(1); 93 | } else { 94 | args->code = strdup(argv[i]); 95 | } 96 | } 97 | } 98 | if (args->filename && !args->code) { 99 | args->code = read_file(args->filename); 100 | } 101 | } 102 | 103 | const char *read_file(const char *filename) 104 | { 105 | /* We need to use binary read on Windows to get file size correctly */ 106 | #ifdef _WIN32 107 | FILE *fp = fopen(filename, "rb"); 108 | #else 109 | FILE *fp = fopen(filename, "r"); 110 | #endif 111 | if (fp == NULL) { 112 | fprintf(stderr, "Failed to open file %s\n", filename); 113 | return NULL; 114 | } 115 | if (fseek(fp, 0, SEEK_END) != 0) { 116 | fprintf(stderr, "Failed to seek to file end\n"); 117 | fclose(fp); 118 | return NULL; 119 | } 120 | long long len = ftell(fp); 121 | if (fseek(fp, 0, SEEK_SET) != 0) { 122 | fprintf(stderr, "Failed to seek to file beginning\n"); 123 | fclose(fp); 124 | return NULL; 125 | } 126 | char *buffer = (char *)calloc(1, len + 10); 127 | if(!buffer) { 128 | fprintf(stderr, "out of memory\n"); 129 | exit(1); 130 | } 131 | size_t n = fread(buffer, 1, len, fp); 132 | if (n == 0) { 133 | fprintf(stderr, "Failed to read file\n"); 134 | fclose(fp); 135 | free(buffer); 136 | return NULL; 137 | } 138 | fclose(fp); 139 | return buffer; 140 | } 141 | 142 | void destroy_arguments(struct arguments *args) 143 | { 144 | free((void *)args->filename); 145 | free((void *)args->code); 146 | } 147 | 148 | void create_allocator(C_MemoryAllocator *allocator) { 149 | allocator->arena = create_mspace(0, 0); 150 | allocator->realloc = mspace_realloc; 151 | allocator->calloc = mspace_calloc; 152 | allocator->free = mspace_free; 153 | allocator->create_arena = create_mspace; 154 | allocator->destroy_arena = destroy_mspace; 155 | } 156 | 157 | void destroy_allocator(C_MemoryAllocator *allocator) { 158 | allocator->destroy_arena(allocator->arena); 159 | } 160 | -------------------------------------------------------------------------------- /src/allocate.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | #ifndef ravicomp_ALLOCATE_H 24 | #define ravicomp_ALLOCATE_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | //#ifdef __cplusplus 32 | //extern "C" { 33 | //#endif 34 | 35 | extern void *raviX_malloc(size_t size); 36 | extern void *raviX_calloc(size_t nmemb, size_t size); 37 | extern void *raviX_realloc(void *ptr, size_t size); 38 | extern void raviX_free(void *ptr); 39 | 40 | #ifndef C_MEMORYALLOCATOR_DEFINED 41 | #define C_MEMORYALLOCATOR_DEFINED 42 | /* Note that this struct below is also defined in ravi_compiler.h/chibicc.h and all 43 | * definitions must be kept in sync. 44 | */ 45 | typedef struct C_MemoryAllocator { 46 | void *arena; 47 | void *(*realloc)(void *arena, void* mem, size_t newsize); 48 | void *(*calloc)(void *arena, size_t n_elements, size_t elem_size); 49 | void (*free)(void *arena, void *p); 50 | void *(*create_arena)(size_t, int); 51 | size_t (*destroy_arena)(void *arena); 52 | } C_MemoryAllocator; 53 | #endif 54 | 55 | /* 56 | Reallocate array from old_n to new_n. If new_n is 0 then array memory is freed. 57 | If new_n is greater than old_n then old data is copied across and the 58 | additional allocated space is zeroed out so caller can rely on the extra space being 59 | initialized to zeros. 60 | */ 61 | extern void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n); 62 | /* 63 | Delete num_to_delete elements starting at starting_index from array of size array_size, where sizeof(each element) is 64 | element_size. The freed up space will be zero initialized. Returns the new array_size. 65 | */ 66 | extern size_t raviX_del_array_element(void *p, size_t element_size, size_t array_size, size_t starting_index, 67 | size_t num_to_delete); 68 | 69 | /* We often want an array of some type with dynamic memory management. The following macros let us 70 | * create such array types and provide simple ways of pushing an element to the array. 71 | */ 72 | #define DECLARE_ARRAY(array_type, TYPE) \ 73 | typedef struct array_type { \ 74 | unsigned capacity; \ 75 | unsigned count; \ 76 | TYPE *data; \ 77 | } array_type 78 | #define array_push(A, type, value) \ 79 | { \ 80 | if ((A)->count == (A)->capacity) { \ 81 | unsigned newsize = (A)->capacity += 10; \ 82 | (A)->data = \ 83 | (type *)raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->capacity, newsize); \ 84 | (A)->capacity = newsize; \ 85 | } \ 86 | (A)->data[(A)->count++] = value; \ 87 | } 88 | #define array_clearmem(A) \ 89 | { \ 90 | raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->capacity, 0); \ 91 | (A)->data = NULL; \ 92 | (A)->capacity = 0; \ 93 | (A)->count = 0; \ 94 | } 95 | 96 | //#ifdef __cplusplus 97 | //} 98 | //#endif 99 | 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![build](https://github.com/dibyendumajumdar/ravi-compiler/workflows/build/badge.svg) 2 | 3 | # ravi-compiler 4 | A compiler for Ravi and Lua that processes Lua/Ravi source code and generates C code. 5 | 6 | ## Goals 7 | 8 | * Create a re-usable Lua/Ravi lexer/parser. 9 | * Define conventional linear intermediate representation (IR). 10 | * Generate C code from Lua/Ravi source. 11 | * Support Ahead of time (AOT) compilation. 12 | * The generated code can be executed by Ravi. Since the generated code depends on various VM structures it is not binary compatible with Lua, but in theory one can modify the code relatively easily to work with Lua 5.3. Lua 5.4 has modifications to the call stack used by Lua which makes it harder to port to. 13 | 14 | ## Modules 15 | 16 | The compiler library consists of distinct modules: 17 | 18 | * lexer (alpha) - responsible for tokenizing an input buffer. 19 | * parser (alpha) - responsible for generating abstract syntax tree (AST). 20 | * AST lowerer (alpha) - currently transforms generic for loops to while loops. 21 | * typechecker (alpha) - responsible for assigning types to variables when possible. 22 | * AST simplifier (alpha) - responsible for performing some initial simplifications such as constant folding, and string concatenation re-writing. 23 | * linearizer (alpha) - responsible for constructing a linear IR representation of the AST. 24 | * optimizer (WIP) - responsible for improving the code, this doesn't do much right now. 25 | * codegenerator (alpha) - responsible for generating C code. Each input is translated to a standalone C file that can be compiled using any C compiler. Ravi can compile this at runtime using MIR C JIT compiler. For AOT compilation, dynamic library needs to be created and a special loader needs to be used that treats the shared library as a compiled version of Lua chunk. The generated C code doesn't use the Lua C call api, as it is designed to look like Lua code. 26 | 27 | ## Status 28 | 29 | ### Limitations 30 | 31 | * No support for var args 32 | 33 | ### Change Log 34 | 35 | * 12-nov-2022 More bug fixes, e.g. repeat statement was incorrectly compiled 36 | * 10-July-2022 Many bug fixes to do with how the virtual registers are allocated 37 | * 12-Oct-2021 Initial proof of concept for [new embedded C syntax](https://github.com/dibyendumajumdar/ravi-compiler/wiki/Embedding-C) 38 | * 22-Jun-2021 Increased coverage of Lua syntax to cover string concatenations and generic for loops. 39 | * 17-Jan-2021 The code is now C++ compliant so we can compile everything in C++ or C. 40 | * 28-Nov-2020 We can generate code for a large subset of Ravi language and run the compiled code from Ravi. 41 | * 01-Dec-2020 The generated code is now also suitable for AOT compilation but requires special loading facility in Ravi. 42 | 43 | ## LICENSE 44 | 45 | The project is available under MIT license. 46 | It includes code from [chibicc](https://github.com/rui314/chibicc) copyrighted by Rui Ueyama. 47 | 48 | ## Documentation 49 | 50 | Documentation is coming soon. 51 | 52 | For now you can look at following: 53 | * [Linear IR](https://github.com/dibyendumajumdar/ravi-compiler/blob/master/docs/linear-ir.md) 54 | * [WIP public api](https://github.com/dibyendumajumdar/ravi-compiler/blob/master/include/ravi_compiler.h) - only the lexer and parser API are public for now 55 | * [Test inputs and outputs](https://github.com/dibyendumajumdar/ravi-compiler/blob/master/tests) 56 | * [Example Ravi Tests](https://github.com/dibyendumajumdar/ravi/tree/master/tests/comptests) 57 | * [AOT Examples](https://github.com/dibyendumajumdar/ravi-compiler/tree/master/examples) 58 | * [CFG Examples](https://github.com/dibyendumajumdar/ravi-compiler/tree/master/docs/cfg) 59 | * See the [Wiki](https://github.com/dibyendumajumdar/ravi-compiler/wiki) for various notes 60 | 61 | ## Why 62 | 63 | Lua's inbuilt parser and code generator is a work of art, very compact and low overhead but extremely fast. It uses minimal memory and produces bytecodes as it parses the source code (single pass compiler). This is great for Lua and Ravi given the use cases of these languages, but makes the parser and code generator hard to understand, play with, or reuse in tools such as IDEs. It also makes it harder to perform any advanced type checking or performance optimizations. 64 | 65 | This project will create a new parser and code generator that is not a replacement for the default one in Lua/Ravi but can be used for more specialised code generation, as well as as a means of understanding how the parser and code generator works. 66 | 67 | ## Technology 68 | 69 | This project is written in C for maximum portability like Lua. 70 | 71 | ## Building 72 | 73 | You will need CMake 3.12 or greater. The build steps are fairly simple on Linux: 74 | 75 | ``` 76 | mkdir build 77 | cd build 78 | cmake .. 79 | make 80 | ``` 81 | 82 | ## Try it out! 83 | 84 | The compiler can be run using the `trun` command line utility. 85 | Example: 86 | 87 | ``` 88 | trun "return 'hello'" 89 | ``` 90 | 91 | To see the C output, try: 92 | 93 | ``` 94 | trun --gen-C "print 'hello world'" 95 | ``` 96 | 97 | ## Testing 98 | 99 | At the moment we have a simple test driver programs: `trun`. The driver takes a string or file input which must be a valid Lua/Ravi chunk of code, and outputs the AST, the result of type checking, linear IR output if supported, and the CFG as a `dot` file. Example of the output can be found in the `tests/expected` folder. 100 | 101 | Suppose `trun` was built in `build` folder then you can run the tests as follows: 102 | 103 | ``` 104 | cd tests && sh truntests.sh ../build/trun 105 | ``` 106 | 107 | The test script compares the output to the expected output. Any difference will cause the test script to fail. 108 | -------------------------------------------------------------------------------- /src/bitset.h: -------------------------------------------------------------------------------- 1 | /* This file is a part of MIR project. 2 | Copyright (C) 2018-2020 Vladimir Makarov . 3 | */ 4 | /****************************************************************************** 5 | * Copyright (C) 2020-2022 Dibyendu Majumdar 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | ******************************************************************************/ 26 | 27 | /* 28 | * Adapted for Ravi Compiler project 29 | */ 30 | 31 | #ifndef ravicomp_BITSET_H 32 | #define ravicomp_BITSET_H 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | //#ifdef __cplusplus 39 | //extern "C" { 40 | //#endif 41 | 42 | typedef uint64_t bitset_el_t; 43 | 44 | typedef struct BitSet { 45 | size_t els_num; 46 | size_t size; 47 | bitset_el_t *varr; 48 | } BitSet; 49 | 50 | extern void raviX_bitset_create2(BitSet *, size_t init_bits_num); 51 | static inline void raviX_bitset_create(BitSet *bm) 52 | { 53 | raviX_bitset_create2(bm, 0); 54 | } 55 | extern void raviX_bitset_destroy(BitSet * bm); 56 | static inline void raviX_bitset_clear(BitSet * bm) 57 | { 58 | bm->els_num = 0; 59 | } 60 | extern int raviX_bitset_bit_p(const BitSet * bm, size_t nb); 61 | /* Sets a bit ON and returns true if previously bit was not set */ 62 | extern int raviX_bitset_set_bit_p(BitSet * bm, size_t bit); 63 | extern int raviX_bitset_clear_bit_p(BitSet * bm, size_t nb); 64 | extern int raviX_bitset_set_or_clear_bit_range_p(BitSet * bm, size_t nb, size_t len, int set_p); 65 | static inline int raviX_bitset_set_bit_range_p(BitSet * bm, size_t nb, size_t len) { 66 | return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, true); 67 | } 68 | static inline int raviX_bitset_clear_bit_range_p(BitSet * bm, size_t nb, size_t len) { 69 | return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, false); 70 | } 71 | extern void raviX_bitset_copy(BitSet * dst, const BitSet * src); 72 | extern int raviX_bitset_equal_p(const BitSet * bm1, const BitSet * bm2); 73 | extern int raviX_bitset_intersect_p(const BitSet * bm1, const BitSet * bm2); 74 | extern int raviX_bitset_empty_p(const BitSet * bm); 75 | /* Return the number of bits set in BM. */ 76 | extern size_t raviX_bitset_bit_count(const BitSet * bm); 77 | extern int raviX_bitset_op2(BitSet * dst, const BitSet * src1, const BitSet * src2, 78 | bitset_el_t (*op) (bitset_el_t, bitset_el_t)); 79 | static inline bitset_el_t raviX_bitset_el_and(bitset_el_t el1, bitset_el_t el2) { return el1 & el2; } 80 | static inline int raviX_bitset_and(BitSet * dst, BitSet * src1, BitSet * src2) { 81 | return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and); 82 | } 83 | static inline bitset_el_t raviX_bitset_el_and_compl(bitset_el_t el1, bitset_el_t el2) { 84 | return el1 & ~el2; 85 | } 86 | static inline int raviX_bitset_and_compl(BitSet * dst, BitSet * src1, BitSet * src2) { 87 | return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and_compl); 88 | } 89 | static inline bitset_el_t raviX_bitset_el_ior(bitset_el_t el1, bitset_el_t el2) { return el1 | el2; } 90 | static inline int raviX_bitset_ior(BitSet * dst, BitSet * src1, BitSet * src2) { 91 | return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_ior); 92 | } 93 | int raviX_bitset_op3(BitSet * dst, const BitSet * src1, const BitSet * src2, 94 | const BitSet * src3, bitset_el_t (*op) (bitset_el_t, bitset_el_t, bitset_el_t)); 95 | static inline bitset_el_t raviX_bitset_el_ior_and(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) { 96 | return el1 | (el2 & el3); 97 | } 98 | /* DST = SRC1 | (SRC2 & SRC3). Return true if DST changed. */ 99 | static inline int raviX_bitset_ior_and(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) { 100 | return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and); 101 | } 102 | static inline bitset_el_t raviX_bitset_el_ior_and_compl(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) { 103 | return el1 | (el2 & ~el3); 104 | } 105 | /* DST = SRC1 | (SRC2 & ~SRC3). Return true if DST changed. */ 106 | static inline int raviX_bitset_ior_and_compl(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) { 107 | return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and_compl); 108 | } 109 | 110 | typedef struct { 111 | BitSet * bitset; 112 | size_t nbit; 113 | } BitSetIterator; 114 | static inline void raviX_bitset_iterator_init(BitSetIterator *iter, BitSet * bitset) { 115 | iter->bitset = bitset; 116 | iter->nbit = 0; 117 | } 118 | extern int raviX_bitset_iterator_next(BitSetIterator *iter, size_t *nbit); 119 | #define FOREACH_BITSET_BIT(iter, bitset, nbit) \ 120 | for (raviX_bitset_iterator_init (&iter, bitset); raviX_bitset_iterator_next (&iter, &nbit);) 121 | 122 | 123 | 124 | 125 | //#ifdef __cplusplus 126 | //} /* extern C */ 127 | //#endif 128 | 129 | #endif 130 | 131 | -------------------------------------------------------------------------------- /docs/cfg/log3.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | LOADGLOBAL {a} {T(1)} 17 | MOV {T(1)} {T(0)} 18 | CBR {T(0)} {L3, L2} 19 | 20 | 21 | 22 | 23 | L3 24 | L3 25 | RET {T(0)} {L1} 26 | 27 | 28 | 29 | 30 | L0->L3 31 | 32 | 33 | 34 | 35 | 36 | L2 37 | L2 38 | LOADGLOBAL {b} {T(2)} 39 | MOV {T(2)} {T(1)} 40 | CBR {T(1)} {L4, L5} 41 | 42 | 43 | 44 | 45 | L0->L2 46 | 47 | 48 | 49 | 50 | 51 | L1 52 | 53 | L1 54 | 55 | 56 | 57 | L3->L1 58 | 59 | 60 | 61 | 62 | 63 | L4 64 | L4 65 | LOADGLOBAL {c} {T(2)} 66 | MOV {T(2)} {T(1)} 67 | BR {L5} 68 | 69 | 70 | 71 | 72 | L2->L4 73 | 74 | 75 | 76 | 77 | 78 | L5 79 | L5 80 | MOV {T(1)} {T(0)} 81 | BR {L3} 82 | 83 | 84 | 85 | 86 | L2->L5 87 | 88 | 89 | 90 | 91 | 92 | L4->L5 93 | 94 | 95 | 96 | 97 | 98 | L5->L3 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /docs/cfg/log4.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Proc1 11 | 12 | 13 | 14 | L0 15 | L0 16 | LOADGLOBAL {a} {T(2)} 17 | MOV {T(2)} {T(1)} 18 | CBR {T(1)} {L4, L5} 19 | 20 | 21 | 22 | 23 | L4 24 | L4 25 | LOADGLOBAL {b} {T(2)} 26 | MOV {T(2)} {T(1)} 27 | BR {L5} 28 | 29 | 30 | 31 | 32 | L0->L4 33 | 34 | 35 | 36 | 37 | 38 | L5 39 | L5 40 | MOV {T(1)} {T(0)} 41 | CBR {T(0)} {L3, L2} 42 | 43 | 44 | 45 | 46 | L0->L5 47 | 48 | 49 | 50 | 51 | 52 | L4->L5 53 | 54 | 55 | 56 | 57 | 58 | L2 59 | L2 60 | LOADGLOBAL {c} {T(1)} 61 | MOV {T(1)} {T(0)} 62 | BR {L3} 63 | 64 | 65 | 66 | 67 | L5->L2 68 | 69 | 70 | 71 | 72 | 73 | L3 74 | L3 75 | RET {T(0)} {L1} 76 | 77 | 78 | 79 | 80 | L5->L3 81 | 82 | 83 | 84 | 85 | 86 | L2->L3 87 | 88 | 89 | 90 | 91 | 92 | L1 93 | 94 | L1 95 | 96 | 97 | 98 | L3->L1 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /tests/tgraph.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2020-2022 Dibyendu Majumdar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include "dominator.h" 25 | #include "graph.h" 26 | #include "ravi_alloc.h" 27 | 28 | static void create_allocator(C_MemoryAllocator *allocator) { 29 | allocator->arena = create_mspace(0, 0); 30 | allocator->realloc = mspace_realloc; 31 | allocator->calloc = mspace_calloc; 32 | allocator->free = mspace_free; 33 | allocator->create_arena = create_mspace; 34 | allocator->destroy_arena = destroy_mspace; 35 | } 36 | 37 | static void destroy_allocator(C_MemoryAllocator *allocator) { 38 | allocator->destroy_arena(allocator->arena); 39 | } 40 | 41 | static int test1(void) 42 | { 43 | int errcount = 0; 44 | C_MemoryAllocator allocator; 45 | create_allocator(&allocator); 46 | Graph *g = raviX_init_graph(0, 2, NULL, &allocator); 47 | raviX_add_edge(g, 0, 1); 48 | raviX_add_edge(g, 1, 2); 49 | if (!raviX_has_edge(g, 0, 1)) 50 | errcount += 1; 51 | if (!raviX_has_edge(g, 1, 2)) 52 | errcount += 1; 53 | if (raviX_has_edge(g, 0, 2)) 54 | errcount += 1; 55 | raviX_draw_graph(g, stdout); 56 | raviX_destroy_graph(g); 57 | destroy_allocator(&allocator); 58 | return errcount; 59 | } 60 | 61 | static Graph *make_graph(C_MemoryAllocator *allocator) 62 | { 63 | Graph *g = raviX_init_graph(0, 5, NULL, allocator); 64 | raviX_add_edge(g, 0, 1); 65 | raviX_add_edge(g, 1, 2); 66 | raviX_add_edge(g, 2, 3); 67 | raviX_add_edge(g, 3, 4); 68 | raviX_add_edge(g, 4, 5); 69 | raviX_add_edge(g, 0, 5); 70 | raviX_add_edge(g, 1, 4); 71 | raviX_add_edge(g, 3, 2); 72 | raviX_add_edge(g, 2, 6); 73 | raviX_add_edge(g, 6, 3); 74 | raviX_add_edge(g, 4, 1); 75 | return g; 76 | } 77 | 78 | static int test2(void) 79 | { 80 | int errcount = 0; 81 | C_MemoryAllocator allocator; 82 | create_allocator(&allocator); 83 | Graph *g = make_graph(&allocator); 84 | raviX_classify_edges(g); 85 | if (raviX_get_edge_type(g, 0, 1) != EDGE_TYPE_TREE) 86 | errcount++; 87 | if (raviX_get_edge_type(g, 1, 2) != EDGE_TYPE_TREE) 88 | errcount++; 89 | if (raviX_get_edge_type(g, 2, 3) != EDGE_TYPE_TREE) 90 | errcount++; 91 | if (raviX_get_edge_type(g, 2, 6) != EDGE_TYPE_TREE) 92 | errcount++; 93 | if (raviX_get_edge_type(g, 3, 4) != EDGE_TYPE_TREE) 94 | errcount++; 95 | if (raviX_get_edge_type(g, 4, 5) != EDGE_TYPE_TREE) 96 | errcount++; 97 | if (raviX_get_edge_type(g, 0, 5) != EDGE_TYPE_FORWARD) 98 | errcount++; 99 | if (raviX_get_edge_type(g, 1, 4) != EDGE_TYPE_FORWARD) 100 | errcount++; 101 | if (raviX_get_edge_type(g, 6, 3) != EDGE_TYPE_CROSS) 102 | errcount++; 103 | if (raviX_get_edge_type(g, 3, 2) != EDGE_TYPE_BACKWARD) 104 | errcount++; 105 | if (raviX_get_edge_type(g, 4, 1) != EDGE_TYPE_BACKWARD) 106 | errcount++; 107 | raviX_draw_graph(g, stdout); 108 | raviX_destroy_graph(g); 109 | destroy_allocator(&allocator); 110 | return errcount; 111 | } 112 | 113 | static Graph *make_graph2(C_MemoryAllocator *allocator) 114 | { 115 | Graph *g = raviX_init_graph(0, 4, NULL, allocator); 116 | raviX_add_edge(g, 0, 1); 117 | raviX_add_edge(g, 1, 2); 118 | raviX_add_edge(g, 1, 5); 119 | raviX_add_edge(g, 2, 3); 120 | raviX_add_edge(g, 5, 6); 121 | raviX_add_edge(g, 5, 8); 122 | raviX_add_edge(g, 6, 7); 123 | raviX_add_edge(g, 8, 7); 124 | raviX_add_edge(g, 7, 3); 125 | raviX_add_edge(g, 3, 1); 126 | raviX_add_edge(g, 3, 4); 127 | return g; 128 | } 129 | 130 | static int test3(void) 131 | { 132 | int errcount = 0; 133 | C_MemoryAllocator allocator; 134 | create_allocator(&allocator); 135 | Graph *g = make_graph2(&allocator); 136 | raviX_classify_edges(g); 137 | DominatorTree *tree = raviX_new_dominator_tree(g); 138 | raviX_calculate_dominator_tree(tree); 139 | raviX_dominator_tree_output(tree, stdout); 140 | raviX_destroy_dominator_tree(tree); 141 | raviX_destroy_graph(g); 142 | destroy_allocator(&allocator); 143 | return errcount; 144 | } 145 | 146 | static int test4(void) 147 | { 148 | int errcount = 0; 149 | C_MemoryAllocator allocator; 150 | create_allocator(&allocator); 151 | Graph *g = make_graph2(&allocator); 152 | if (raviX_node_list_size(raviX_successors(raviX_graph_node(g, 1))) != 2) 153 | errcount++; 154 | if (raviX_node_list_size(raviX_successors(raviX_graph_node(g, 2))) != 1) 155 | errcount++; 156 | raviX_delete_edge(g, 1, 2); 157 | GraphNodeList *succ = raviX_successors(raviX_graph_node(g, 1)); 158 | if (raviX_node_list_size(succ) != 1) 159 | errcount++; 160 | if (raviX_node_list_at(succ, 0) != 5) 161 | errcount++; 162 | GraphNodeList *preds = raviX_predecessors(raviX_graph_node(g, 2)); 163 | if (raviX_node_list_size(preds) != 0) 164 | errcount++; 165 | raviX_destroy_graph(g); 166 | destroy_allocator(&allocator); 167 | return errcount; 168 | } 169 | 170 | int main(int argc, const char *argv[]) 171 | { 172 | int errcount = test1(); 173 | errcount += test2(); 174 | errcount += test3(); 175 | errcount += test4(); 176 | if (errcount == 0) 177 | printf("Ok\n"); 178 | else 179 | printf("Failed\n"); 180 | return errcount == 0 ? 0 : 1; 181 | } --------------------------------------------------------------------------------