├── 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 | L0
4 | LOADGLOBAL {b} {T(0)}
5 | NOT {T(0)} {T(1)}
6 | RET {T(1)} {L1}
7 |
>];
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=<>];
6 | L0 -> L3
7 | L3 [shape=none, margin=0, label=<
8 | L3
9 | LOADGLOBAL {print} {T(0)}
10 | CALL {T(0), 'forever' Ks(0)} {T(0..)}
11 | BR {L4}
12 |
>];
13 | L3 -> L4
14 | L4 [shape=none, margin=0, label=<
15 | L4
16 | RET {L1}
17 |
>];
18 | L4 -> L1
19 | }
20 |
--------------------------------------------------------------------------------
/docs/cfg/log1.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<
3 | L0
4 | LOADGLOBAL {a} {T(1)}
5 | MOV {T(1)} {T(0)}
6 | CBR {T(0)} {L2, L3}
7 |
>];
8 | L0 -> L2
9 | L0 -> L3
10 | L2 [shape=none, margin=0, label=<
11 | L2
12 | LOADGLOBAL {b} {T(1)}
13 | MOV {T(1)} {T(0)}
14 | BR {L3}
15 |
>];
16 | L2 -> L3
17 | L3 [shape=none, margin=0, label=<
18 | L3
19 | RET {T(0)} {L1}
20 |
>];
21 | L3 -> L1
22 | }
23 |
--------------------------------------------------------------------------------
/docs/cfg/log2.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<
3 | L0
4 | LOADGLOBAL {a} {T(1)}
5 | MOV {T(1)} {T(0)}
6 | CBR {T(0)} {L3, L2}
7 |
>];
8 | L0 -> L3
9 | L0 -> L2
10 | L2 [shape=none, margin=0, label=<
11 | L2
12 | LOADGLOBAL {b} {T(1)}
13 | MOV {T(1)} {T(0)}
14 | BR {L3}
15 |
>];
16 | L2 -> L3
17 | L3 [shape=none, margin=0, label=<
18 | L3
19 | RET {T(0)} {L1}
20 |
>];
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=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | CBR {true} {L3, L4}
10 |
>];
11 | L2 -> L3
12 | L2 -> L4
13 | L3 [shape=none, margin=0, label=<
14 | L3
15 | LOADGLOBAL {print} {T(0)}
16 | CALL {T(0), 'forever' Ks(0)} {T(0..)}
17 | BR {L2}
18 |
>];
19 | L3 -> L2
20 | L4 [shape=none, margin=0, label=<
21 | L4
22 | BR {L1}
23 |
>];
24 | L4 -> L1
25 | }
26 |
--------------------------------------------------------------------------------
/docs/cfg/if1.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | LIii {1 Kint(0), 2 Kint(1)} {T(0)}
10 | CBR {T(0)} {L3, L4}
11 |
>];
12 | L2 -> L3
13 | L2 -> L4
14 | L3 [shape=none, margin=0, label=<
15 | L3
16 | LOADGLOBAL {print} {T(1)}
17 | CALL {T(1), 'hi' Ks(2)} {T(1..)}
18 | BR {L4}
19 |
>];
20 | L3 -> L4
21 | L4 [shape=none, margin=0, label=<
22 | L4
23 | BR {L1}
24 |
>];
25 | L4 -> L1
26 | }
27 |
--------------------------------------------------------------------------------
/docs/cfg/while2.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | LT {local(a, 0), 10 Kint(0)} {T(0)}
10 | CBR {T(0)} {L3, L4}
11 |
>];
12 | L2 -> L3
13 | L2 -> L4
14 | L3 [shape=none, margin=0, label=<
15 | L3
16 | ADD {local(a, 0), 1 Kint(1)} {T(0)}
17 | MOV {T(0)} {local(a, 0)}
18 | BR {L2}
19 |
>];
20 | L3 -> L2
21 | L4 [shape=none, margin=0, label=<
22 | L4
23 | BR {L1}
24 |
>];
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=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | LIii {1 Kint(0), 2 Kint(1)} {T(0)}
10 | CBR {T(0)} {L3, L4}
11 |
>];
12 | L2 -> L3
13 | L2 -> L4
14 | L3 [shape=none, margin=0, label=<
15 | L3
16 | LOADGLOBAL {print} {T(1)}
17 | CALL {T(1), 'hi' Ks(2)} {T(1..)}
18 | BR {L5}
19 |
>];
20 | L3 -> L5
21 | L4 [shape=none, margin=0, label=<
22 | L4
23 | LOADGLOBAL {print} {T(2)}
24 | CALL {T(2), 'no' Ks(3)} {T(2..)}
25 | BR {L5}
26 |
>];
27 | L4 -> L5
28 | L5 [shape=none, margin=0, label=<
29 | L5
30 | BR {L1}
31 |
>];
32 | L5 -> L1
33 | }
34 |
--------------------------------------------------------------------------------
/docs/cfg/log3.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<
3 | L0
4 | LOADGLOBAL {a} {T(1)}
5 | MOV {T(1)} {T(0)}
6 | CBR {T(0)} {L3, L2}
7 |
>];
8 | L0 -> L3
9 | L0 -> L2
10 | L2 [shape=none, margin=0, label=<
11 | L2
12 | LOADGLOBAL {b} {T(2)}
13 | MOV {T(2)} {T(1)}
14 | CBR {T(1)} {L4, L5}
15 |
>];
16 | L2 -> L4
17 | L2 -> L5
18 | L3 [shape=none, margin=0, label=<
19 | L3
20 | RET {T(0)} {L1}
21 |
>];
22 | L3 -> L1
23 | L4 [shape=none, margin=0, label=<
24 | L4
25 | LOADGLOBAL {c} {T(2)}
26 | MOV {T(2)} {T(1)}
27 | BR {L5}
28 |
>];
29 | L4 -> L5
30 | L5 [shape=none, margin=0, label=<
31 | L5
32 | MOV {T(1)} {T(0)}
33 | BR {L3}
34 |
>];
35 | L5 -> L3
36 | }
37 |
--------------------------------------------------------------------------------
/docs/cfg/log4.gv:
--------------------------------------------------------------------------------
1 | digraph Proc1 {
2 | L0 [shape=none, margin=0, label=<
3 | L0
4 | LOADGLOBAL {a} {T(2)}
5 | MOV {T(2)} {T(1)}
6 | CBR {T(1)} {L4, L5}
7 |
>];
8 | L0 -> L4
9 | L0 -> L5
10 | L2 [shape=none, margin=0, label=<
11 | L2
12 | LOADGLOBAL {c} {T(1)}
13 | MOV {T(1)} {T(0)}
14 | BR {L3}
15 |
>];
16 | L2 -> L3
17 | L3 [shape=none, margin=0, label=<
18 | L3
19 | RET {T(0)} {L1}
20 |
>];
21 | L3 -> L1
22 | L4 [shape=none, margin=0, label=<
23 | L4
24 | LOADGLOBAL {b} {T(2)}
25 | MOV {T(2)} {T(1)}
26 | BR {L5}
27 |
>];
28 | L4 -> L5
29 | L5 [shape=none, margin=0, label=<
30 | L5
31 | MOV {T(1)} {T(0)}
32 | CBR {T(0)} {L3, L2}
33 |
>];
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 | L0
4 | MOV {1 Kint(0)} {Tint(1)}
5 | MOV {10 Kint(1)} {Tint(2)}
6 | MOV {1 Kint(0)} {Tint(3)}
7 | LIii {0 Kint(2), Tint(3)} {Tint(4)}
8 | SUBii {Tint(1), Tint(3)} {Tint(1)}
9 | BR {L2}
10 |
>];
11 | L0 -> L2
12 | L2 [shape=none, margin=0, label=<
13 | L2
14 | ADDii {Tint(1), Tint(3)} {Tint(1)}
15 | CBR {Tint(4)} {L3, L4}
16 |
>];
17 | L2 -> L3
18 | L2 -> L4
19 | L3 [shape=none, margin=0, label=<
20 | L3
21 | LEii {Tint(2), Tint(1)} {Tint(5)}
22 | CBR {Tint(5)} {L6, L5}
23 |
>];
24 | L3 -> L6
25 | L3 -> L5
26 | L4 [shape=none, margin=0, label=<
27 | L4
28 | LIii {Tint(1), Tint(2)} {Tint(5)}
29 | CBR {Tint(5)} {L6, L5}
30 |
>];
31 | L4 -> L6
32 | L4 -> L5
33 | L5 [shape=none, margin=0, label=<
34 | L5
35 | MOV {Tint(1)} {Tint(0)}
36 | LOADGLOBAL {print} {T(0)}
37 | CALL {T(0), Tint(0)} {T(0..)}
38 | BR {L2}
39 |
>];
40 | L5 -> L2
41 | L6 [shape=none, margin=0, label=<
42 | L6
43 | BR {L1}
44 |
>];
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 | L0
4 | MOV {1 Kint(0)} {Tint(1)}
5 | MOV {10 Kint(1)} {Tint(2)}
6 | MOV {1 Kint(0)} {Tint(3)}
7 | LIii {0 Kint(2), Tint(3)} {Tint(4)}
8 | SUBii {Tint(1), Tint(3)} {Tint(1)}
9 | BR {L2}
10 |
>];
11 | L0 -> L2
12 | L2 [shape=none, margin=0, label=<
13 | L2
14 | ADDii {Tint(1), Tint(3)} {Tint(1)}
15 | CBR {Tint(4)} {L3, L4}
16 |
>];
17 | L2 -> L3
18 | L2 -> L4
19 | L3 [shape=none, margin=0, label=<
20 | L3
21 | LEii {Tint(2), Tint(1)} {Tint(5)}
22 | CBR {Tint(5)} {L6, L5}
23 |
>];
24 | L3 -> L6
25 | L3 -> L5
26 | L4 [shape=none, margin=0, label=<
27 | L4
28 | LIii {Tint(1), Tint(2)} {Tint(5)}
29 | CBR {Tint(5)} {L6, L5}
30 |
>];
31 | L4 -> L6
32 | L4 -> L5
33 | L5 [shape=none, margin=0, label=<
34 | L5
35 | MOV {Tint(1)} {Tint(0)}
36 | BR {L7}
37 |
>];
38 | L5 -> L7
39 | L6 [shape=none, margin=0, label=<
40 | L6
41 | BR {L1}
42 |
>];
43 | L6 -> L1
44 | L7 [shape=none, margin=0, label=<
45 | L7
46 | EQii {Tint(0), 2 Kint(3)} {T(0)}
47 | CBR {T(0)} {L8, L9}
48 |
>];
49 | L7 -> L8
50 | L7 -> L9
51 | L8 [shape=none, margin=0, label=<
52 | L8
53 | BR {L6}
54 |
>];
55 | L8 -> L6
56 | L9 [shape=none, margin=0, label=<
57 | L9
58 | BR {L2}
59 |
>];
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=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | LOADGLOBAL {a} {T(0)}
10 | LOADGLOBAL {b} {T(1)}
11 | LT {T(0), T(1)} {T(2)}
12 | CBR {T(2)} {L5, L3}
13 |
>];
14 | L2 -> L5
15 | L2 -> L3
16 | L3 [shape=none, margin=0, label=<
17 | L3
18 | LOADGLOBAL {a} {T(1)}
19 | LOADGLOBAL {b} {T(0)}
20 | EQ {T(1), T(0)} {T(3)}
21 | CBR {T(3)} {L6, L4}
22 |
>];
23 | L3 -> L6
24 | L3 -> L4
25 | L4 [shape=none, margin=0, label=<
26 | L4
27 | LOADGLOBAL {a} {T(0)}
28 | LOADGLOBAL {b} {T(1)}
29 | LE {T(1), T(0)} {T(4)}
30 | CBR {T(4)} {L7, L8}
31 |
>];
32 | L4 -> L7
33 | L4 -> L8
34 | L5 [shape=none, margin=0, label=<
35 | L5
36 | LOADGLOBAL {print} {T(0)}
37 | CALL {T(0), 'case 1' Ks(0)} {T(0..)}
38 | BR {L9}
39 |
>];
40 | L5 -> L9
41 | L6 [shape=none, margin=0, label=<
42 | L6
43 | LOADGLOBAL {print} {T(1)}
44 | CALL {T(1), 'case2' Ks(1)} {T(1..)}
45 | BR {L9}
46 |
>];
47 | L6 -> L9
48 | L7 [shape=none, margin=0, label=<
49 | L7
50 | LOADGLOBAL {print} {T(5)}
51 | CALL {T(5), 'case 3' Ks(2)} {T(5..)}
52 | BR {L9}
53 |
>];
54 | L7 -> L9
55 | L8 [shape=none, margin=0, label=<
56 | L8
57 | LOADGLOBAL {print} {T(6)}
58 | CALL {T(6), 'last case' Ks(3)} {T(6..)}
59 | BR {L9}
60 |
>];
61 | L8 -> L9
62 | L9 [shape=none, margin=0, label=<
63 | L9
64 | BR {L1}
65 |
>];
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=<>];
6 | L0 -> L2
7 | L2 [shape=none, margin=0, label=<
8 | L2
9 | EQ {local(a, 0), local(b, 1)} {T(0)}
10 | CBR {T(0)} {L5, L3}
11 |
>];
12 | L2 -> L5
13 | L2 -> L3
14 | L3 [shape=none, margin=0, label=<
15 | L3
16 | EQ {local(a, 0), local(c, 2)} {T(1)}
17 | CBR {T(1)} {L6, L4}
18 |
>];
19 | L3 -> L6
20 | L3 -> L4
21 | L4 [shape=none, margin=0, label=<
22 | L4
23 | EQ {local(a, 0), local(d, 3)} {T(2)}
24 | CBR {T(2)} {L7, L8}
25 |
>];
26 | L4 -> L7
27 | L4 -> L8
28 | L5 [shape=none, margin=0, label=<
29 | L5
30 | BR {L10}
31 |
>];
32 | L5 -> L10
33 | L6 [shape=none, margin=0, label=<
34 | L6
35 | BR {L12}
36 |
>];
37 | L6 -> L12
38 | L7 [shape=none, margin=0, label=<
39 | L7
40 | BR {L12}
41 |
>];
42 | L7 -> L12
43 | L8 [shape=none, margin=0, label=<
44 | L8
45 | BR {L15}
46 |
>];
47 | L8 -> L15
48 | L10 [shape=none, margin=0, label=<
49 | L10
50 | LOADGLOBAL {print} {T(4)}
51 | CALL {T(4), 'hello' Ks(0)} {T(4..)}
52 | BR {L12}
53 |
>];
54 | L10 -> L12
55 | L12 [shape=none, margin=0, label=<
56 | L12
57 | BR {L19}
58 |
>];
59 | L12 -> L19
60 | L15 [shape=none, margin=0, label=<
61 | L15
62 | EQ {local(a, 0), local(e, 4)} {T(3)}
63 | CBR {T(3)} {L16, L17}
64 |
>];
65 | L15 -> L16
66 | L15 -> L17
67 | L16 [shape=none, margin=0, label=<
68 | L16
69 | BR {L19}
70 |
>];
71 | L16 -> L19
72 | L17 [shape=none, margin=0, label=<
73 | L17
74 | BR {L19}
75 |
>];
76 | L17 -> L19
77 | L19 [shape=none, margin=0, label=<
78 | L19
79 | BR {L1}
80 |
>];
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 | L0
4 | MOV {1 Kint(0)} {Tint(1)}
5 | MOV {10 Kint(1)} {Tint(2)}
6 | MOV {1 Kint(0)} {Tint(3)}
7 | LIii {0 Kint(2), Tint(3)} {Tint(4)}
8 | SUBii {Tint(1), Tint(3)} {Tint(1)}
9 | BR {L2}
10 |
>];
11 | L0 -> L2
12 | L2 [shape=none, margin=0, label=<
13 | L2
14 | ADDii {Tint(1), Tint(3)} {Tint(1)}
15 | CBR {Tint(4)} {L3, L4}
16 |
>];
17 | L2 -> L3
18 | L2 -> L4
19 | L3 [shape=none, margin=0, label=<
20 | L3
21 | LEii {Tint(2), Tint(1)} {Tint(5)}
22 | CBR {Tint(5)} {L6, L5}
23 |
>];
24 | L3 -> L6
25 | L3 -> L5
26 | L4 [shape=none, margin=0, label=<
27 | L4
28 | LIii {Tint(1), Tint(2)} {Tint(5)}
29 | CBR {Tint(5)} {L6, L5}
30 |
>];
31 | L4 -> L6
32 | L4 -> L5
33 | L5 [shape=none, margin=0, label=<
34 | L5
35 | MOV {Tint(1)} {Tint(0)}
36 | MOV {10 Kint(1)} {Tint(7)}
37 | MOV {1 Kint(0)} {Tint(8)}
38 | UNMi {1 Kint(0)} {Tint(9)}
39 | MOV {Tint(9)} {Tint(10)}
40 | LIii {0 Kint(2), Tint(10)} {Tint(11)}
41 | SUBii {Tint(7), Tint(10)} {Tint(7)}
42 | BR {L7}
43 |
>];
44 | L5 -> L7
45 | L6 [shape=none, margin=0, label=<
46 | L6
47 | BR {L1}
48 |
>];
49 | L6 -> L1
50 | L7 [shape=none, margin=0, label=<
51 | L7
52 | ADDii {Tint(7), Tint(10)} {Tint(7)}
53 | CBR {Tint(11)} {L8, L9}
54 |
>];
55 | L7 -> L8
56 | L7 -> L9
57 | L8 [shape=none, margin=0, label=<
58 | L8
59 | LEii {Tint(8), Tint(7)} {Tint(12)}
60 | CBR {Tint(12)} {L11, L10}
61 |
>];
62 | L8 -> L11
63 | L8 -> L10
64 | L9 [shape=none, margin=0, label=<
65 | L9
66 | LIii {Tint(7), Tint(8)} {Tint(12)}
67 | CBR {Tint(12)} {L11, L10}
68 |
>];
69 | L9 -> L11
70 | L9 -> L10
71 | L10 [shape=none, margin=0, label=<
72 | L10
73 | MOV {Tint(7)} {Tint(6)}
74 | LOADGLOBAL {print} {T(0)}
75 | CALL {T(0), Tint(0), Tint(6)} {T(0..)}
76 | BR {L7}
77 |
>];
78 | L10 -> L7
79 | L11 [shape=none, margin=0, label=<
80 | L11
81 | BR {L2}
82 |
>];
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 | L0
95 | LOADGLOBAL {print} {T(0)}
96 | CALL {T(0), 'hello world' Ks(0)} {T(0..)}
97 | BR {L1}
98 |
>];
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 | L0
4 | MOV {1 Kint(0)} {Tint(1)}
5 | MOV {10 Kint(1)} {Tint(2)}
6 | MOV {1 Kint(0)} {Tint(3)}
7 | LIii {0 Kint(2), Tint(3)} {Tint(4)}
8 | SUBii {Tint(1), Tint(3)} {Tint(1)}
9 | BR {L2}
10 |
>];
11 | L0 -> L2
12 | L2 [shape=none, margin=0, label=<
13 | L2
14 | ADDii {Tint(1), Tint(3)} {Tint(1)}
15 | CBR {Tint(4)} {L3, L4}
16 |
>];
17 | L2 -> L3
18 | L2 -> L4
19 | L3 [shape=none, margin=0, label=<
20 | L3
21 | LEii {Tint(2), Tint(1)} {Tint(5)}
22 | CBR {Tint(5)} {L6, L5}
23 |
>];
24 | L3 -> L6
25 | L3 -> L5
26 | L4 [shape=none, margin=0, label=<
27 | L4
28 | LIii {Tint(1), Tint(2)} {Tint(5)}
29 | CBR {Tint(5)} {L6, L5}
30 |
>];
31 | L4 -> L6
32 | L4 -> L5
33 | L5 [shape=none, margin=0, label=<
34 | L5
35 | MOV {Tint(1)} {Tint(0)}
36 | MOV {10 Kint(1)} {Tint(7)}
37 | MOV {1 Kint(0)} {Tint(8)}
38 | UNMi {1 Kint(0)} {Tint(9)}
39 | MOV {Tint(9)} {Tint(10)}
40 | LIii {0 Kint(2), Tint(10)} {Tint(11)}
41 | SUBii {Tint(7), Tint(10)} {Tint(7)}
42 | BR {L7}
43 |
>];
44 | L5 -> L7
45 | L6 [shape=none, margin=0, label=<
46 | L6
47 | BR {L1}
48 |
>];
49 | L6 -> L1
50 | L7 [shape=none, margin=0, label=<
51 | L7
52 | ADDii {Tint(7), Tint(10)} {Tint(7)}
53 | CBR {Tint(11)} {L8, L9}
54 |
>];
55 | L7 -> L8
56 | L7 -> L9
57 | L8 [shape=none, margin=0, label=<
58 | L8
59 | LEii {Tint(8), Tint(7)} {Tint(12)}
60 | CBR {Tint(12)} {L11, L10}
61 |
>];
62 | L8 -> L11
63 | L8 -> L10
64 | L9 [shape=none, margin=0, label=<
65 | L9
66 | LIii {Tint(7), Tint(8)} {Tint(12)}
67 | CBR {Tint(12)} {L11, L10}
68 |
>];
69 | L9 -> L11
70 | L9 -> L10
71 | L10 [shape=none, margin=0, label=<
72 | L10
73 | MOV {Tint(7)} {Tint(6)}
74 | MOV {1 Kint(0)} {Tint(14)}
75 | MOV {3 Kint(3)} {Tint(15)}
76 | MOV {2 Kint(4)} {Tint(16)}
77 | LIii {0 Kint(2), Tint(16)} {Tint(17)}
78 | SUBii {Tint(14), Tint(16)} {Tint(14)}
79 | BR {L12}
80 |
>];
81 | L10 -> L12
82 | L11 [shape=none, margin=0, label=<
83 | L11
84 | BR {L2}
85 |
>];
86 | L11 -> L2
87 | L12 [shape=none, margin=0, label=<
88 | L12
89 | ADDii {Tint(14), Tint(16)} {Tint(14)}
90 | CBR {Tint(17)} {L13, L14}
91 |
>];
92 | L12 -> L13
93 | L12 -> L14
94 | L13 [shape=none, margin=0, label=<
95 | L13
96 | LEii {Tint(15), Tint(14)} {Tint(18)}
97 | CBR {Tint(18)} {L16, L15}
98 |
>];
99 | L13 -> L16
100 | L13 -> L15
101 | L14 [shape=none, margin=0, label=<
102 | L14
103 | LIii {Tint(14), Tint(15)} {Tint(18)}
104 | CBR {Tint(18)} {L16, L15}
105 |
>];
106 | L14 -> L16
107 | L14 -> L15
108 | L15 [shape=none, margin=0, label=<
109 | L15
110 | MOV {Tint(14)} {Tint(13)}
111 | LOADGLOBAL {print} {T(0)}
112 | CALL {T(0), Tint(0), Tint(6), Tint(13)} {T(0..)}
113 | BR {L12}
114 |
>];
115 | L15 -> L12
116 | L16 [shape=none, margin=0, label=<
117 | L16
118 | BR {L7}
119 |
>];
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 | 
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 | }
--------------------------------------------------------------------------------