├── .gitignore ├── Compiler.old ├── .gitignore ├── Makefile ├── README.rst ├── apps │ ├── uasm │ │ ├── .gitignore │ │ ├── include │ │ │ └── uasm.hrl │ │ └── src │ │ │ ├── uasm.app.src │ │ │ ├── uasm.erl │ │ │ ├── uasm_bytecode.erl │ │ │ ├── uasm_encode_cte.erl │ │ │ ├── uasm_encode_int.erl │ │ │ ├── uasm_file.erl │ │ │ ├── uasm_huffman.erl │ │ │ ├── uasm_pass_asm.erl │ │ │ ├── uasm_stats.erl │ │ │ └── uasm_util.erl │ └── uerlc │ │ ├── .gitignore │ │ ├── include │ │ └── uerlc.hrl │ │ └── src │ │ ├── uerlc.app.src │ │ ├── uerlc.erl │ │ ├── uerlc_compile.erl │ │ ├── uerlc_pass_from_S.erl │ │ └── uerlc_print.erl ├── elvis.config ├── rebar.config └── uerlc.sh ├── Compiler ├── .gitignore ├── .stylish-haskell.yaml ├── LICENSE ├── Makefile ├── README.rst ├── Setup.hs ├── app │ └── Main.hs ├── src │ ├── Asm.hs │ ├── Asm │ │ ├── Binary.hs │ │ ├── Func.hs │ │ ├── Instruction.hs │ │ ├── Locations.hs │ │ └── Mod.hs │ ├── BeamSParser.hs │ ├── Bitcode.hs │ ├── Bitcode │ │ ├── Encode.hs │ │ ├── Encode │ │ │ ├── Const.hs │ │ │ └── Huffman.hs │ │ ├── Func.hs │ │ ├── Mod.hs │ │ ├── Op.hs │ │ └── UerlFile.hs │ ├── Bits.hs │ ├── Pass │ │ ├── PassAsm.hs │ │ ├── PassBeamS.hs │ │ └── PassBitcode.hs │ ├── Term.hs │ └── Uerlc.hs ├── stack.yaml ├── test │ └── Spec.hs ├── testData │ ├── .gitignore │ ├── gb_trees.erl │ ├── mochijson.erl │ ├── mochijson2.erl │ └── test1.erl └── uerlc.cabal ├── LICENSE ├── Makefile ├── README.rst ├── Runtime ├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── Makefile ├── PVS-Studio.cfg ├── PVS-Studio.cmake ├── doc │ └── 3eam-format.rst ├── format.sh ├── include │ ├── e4.h │ ├── e4platf │ │ ├── byte_stream_reader.h │ │ ├── conf.h │ │ ├── debug.h │ │ ├── fs.h │ │ ├── mem.h │ │ ├── messages.h │ │ └── types.h │ ├── e4rt │ │ ├── atom_store.h │ │ ├── binary.h │ │ ├── box.h │ │ ├── bytecode.h │ │ ├── code_mgr.h │ │ ├── dist.h │ │ ├── ext_term_format.h │ │ ├── heap.h │ │ ├── messages.h │ │ ├── module.h │ │ ├── process.h │ │ ├── range_checker.h │ │ ├── scheduler.h │ │ ├── term.h │ │ ├── term_as_map_key.h │ │ ├── term_tag.h │ │ └── vm.h │ └── e4std │ │ ├── array.h │ │ ├── complicated.h │ │ ├── free_fun.h │ │ ├── map.h │ │ ├── ptr.h │ │ ├── sizes.h │ │ ├── string.h │ │ ├── stuff.h │ │ ├── vector.h │ │ ├── vector_impl.h │ │ └── view.h ├── license.snippet └── src │ ├── e4platf │ ├── byte_stream_reader.cpp │ ├── debug.cpp │ ├── fs.cpp │ └── mem.cpp │ ├── e4rt │ ├── atom_store.cpp │ ├── binary.cpp │ ├── bytecode.cpp │ ├── code_mgr.cpp │ ├── ext_term_format.cpp │ ├── module.cpp │ ├── process.cpp │ ├── scheduler.cpp │ ├── term.cpp │ ├── vm.cpp │ └── vm_loop.cpp │ ├── e4std │ ├── free_fun.cpp │ ├── stuff.cpp │ └── vector.cpp │ └── main.cpp ├── docs-src ├── .gitignore ├── Makefile ├── _static │ └── uerl.png ├── _templates │ ├── layout.html │ └── page.html ├── compact-encoding.rst ├── conf.py ├── contributing.rst ├── getting-started.rst ├── index.rst └── make.bat └── docs ├── .buildinfo ├── .nojekyll ├── CNAME ├── _sources ├── compact-encoding.rst.txt ├── contributing.rst.txt ├── getting-started.rst.txt └── index.rst.txt ├── _static ├── ajax-loader.gif ├── alabaster.css ├── basic.css ├── comment-bright.png ├── comment-close.png ├── comment.png ├── custom.css ├── doctools.js ├── down-pressed.png ├── down.png ├── file.png ├── jquery-3.1.0.js ├── jquery.js ├── minus.png ├── plus.png ├── pygments.css ├── searchtools.js ├── uerl.png ├── underscore-1.3.1.js ├── underscore.js ├── up-pressed.png ├── up.png └── websupport.js ├── compact-encoding.html ├── contributing.html ├── genindex.html ├── getting-started.html ├── index.html ├── objects.inv ├── search.html └── searchindex.js /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build*/ 2 | *.S 3 | *.beam 4 | *.dump 5 | *.crashdump 6 | 7 | .idea/ 8 | *.iml 9 | rebar.lock 10 | 11 | # compiled MicroErlang binaries 12 | *.uerl 13 | 14 | # PVS-studio analysis results 15 | PVS-report.tasks 16 | valgrind.log 17 | -------------------------------------------------------------------------------- /Compiler.old/.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary files used for whatever reasons 2 | *.txt 3 | e4c_pass*.txt 4 | j1c_pass*.txt 5 | 6 | # Erlang temporary and build output 7 | .rebar3 8 | _* 9 | .eunit 10 | *.o 11 | *.beam 12 | *.plt 13 | *.swp 14 | *.swo 15 | .erlang.cookie 16 | ebin 17 | log 18 | erl_crash.dump 19 | .rebar 20 | logs 21 | _build 22 | .idea 23 | 24 | # Rebar and IntelliJ Output directories 25 | deps/* 26 | out/* 27 | classes/* 28 | -------------------------------------------------------------------------------- /Compiler.old/Makefile: -------------------------------------------------------------------------------- 1 | TESTFILE=gb_trees 2 | TESTFILE2=test1 3 | TESTFILE3=mochijson 4 | TESTFILE4=mochijson2 5 | 6 | .PHONY: run1 run2 run3 run4 7 | run1: 8 | rebar3 compile && ./uerlc.sh priv/$(TESTFILE).erl 9 | run2: 10 | rebar3 compile && ./uerlc.sh priv/$(TESTFILE2).erl 11 | run3: 12 | rebar3 compile && ./uerlc.sh priv/$(TESTFILE3).erl 13 | run4: 14 | rebar3 compile && ./uerlc.sh priv/$(TESTFILE4).erl 15 | 16 | .PHONY: dialyze 17 | dialyze: 18 | rebar3 dialyzer 19 | 20 | .PHONY: test 21 | test: 22 | rebar3 ct 23 | -------------------------------------------------------------------------------- /Compiler.old/README.rst: -------------------------------------------------------------------------------- 1 | MicroErlang Transpiler 2 | ====================== 3 | 4 | A simple transpiler, which picks up output from one late BEAM compiler stage 5 | (namely: the BEAM assembly output). 6 | It is then converted into simplified bytecode for the MicroErlang 7 | virtual machine. 8 | The code is compressed with a mix of Huffman codes and bit field magic 9 | and is designed to be executed without decompression. 10 | 11 | Trying it out 12 | ------------- 13 | 14 | $ make run1 15 | $ make run2 16 | $ make run3 17 | 18 | This will compile included example modules from the `priv/` dir producing 19 | `*.uerl` output files. 20 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | .idea 17 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/include/uasm.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(E4_ASSEMBLY_INC). 2 | -define(E4_ASSEMBLY_INC, 1). 3 | 4 | -type label() :: {f, integer()}. 5 | -type xreg() :: {x, integer()}. 6 | 7 | -type e4mod() :: #{ 8 | '$' => module, 9 | funs => orddict:orddict() 10 | }. 11 | 12 | -type e4fun() :: #{ 13 | '$' => e4fun, 14 | name => atom(), 15 | arity => integer(), 16 | code => list() 17 | }. 18 | 19 | -type e4bif() :: #{ 20 | '$' => e4bif, 21 | name => atom(), 22 | fail => label() | ignore, % default ignore 23 | args => list(), 24 | result => xreg() | ignore, % default ignore 25 | gc => integer() % heap size to ensure, default 0 26 | }. 27 | 28 | -type e4call() :: #{ 29 | '$' => e4call, 30 | target => label(), 31 | arity => integer(), 32 | tailcall => boolean(), % default false 33 | dealloc => integer() % default 0 34 | }. 35 | 36 | -type e4ret() :: #{ 37 | '$' => e4ret, 38 | dealloc => integer() % default 0 39 | }. 40 | 41 | -endif. 42 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/src/uasm.app.src: -------------------------------------------------------------------------------- 1 | {application, uasm, 2 | [{description, "MicroErlang Assembly compiler"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env, []}, 10 | {modules, []}, 11 | 12 | {maintainers, []}, 13 | {licenses, []}, 14 | {links, []} 15 | ]}. 16 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/src/uasm.erl: -------------------------------------------------------------------------------- 1 | -module(uasm). 2 | -export([process/2]). 3 | 4 | %% @doc Takes: A module map #{} with postprocessed BEAM assembly with new 5 | %% MicroErlang ASM instructions in it 6 | %% Result: Writes binary `.uerl` file with bytecode and optional `.stats.txt` 7 | %% file with frequency stats collected by `uasm_stats` module. 8 | -spec process(string(), #{}) -> ok. 9 | process(InputPath, Input) -> 10 | uasm_stats:init(), % frequency stats are collected here 11 | BC = uerlc:try_do( 12 | "uasm_pass_asm - Assembly to binary", 13 | fun() -> uasm_pass_asm:compile(Input) end 14 | ), 15 | OutputPath = uasm_file:make_filename(InputPath, "uerl"), 16 | save_output(text, BC, OutputPath), 17 | StatsOutputPath = uasm_file:make_filename(InputPath, "stats.txt"), 18 | uasm_stats:dump(StatsOutputPath). 19 | 20 | 21 | %% @doc Format the resulting bytecode as text (for debug and simplicity) or 22 | %% as a binary (for final deployment). 23 | save_output(Format, BC, OutputPath) -> 24 | uerlc:try_do("Save " ++ erlang:atom_to_list(Format) ++ " output", 25 | fun() -> 26 | IOList = uasm_file:to_iolist(Format, BC), 27 | file:write_file(OutputPath, iolist_to_binary(IOList)) 28 | end), 29 | ok. 30 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/src/uasm_encode_cte.erl: -------------------------------------------------------------------------------- 1 | %%% @doc BEAM Compact Term encoding, also used in Ericsson's BEAM files 2 | %%% @end 3 | 4 | -module(uasm_encode_cte). 5 | 6 | %% API 7 | -export([encode/2]). 8 | 9 | -include_lib("uerlc/include/uerlc.hrl"). 10 | -include_lib("compiler/src/beam_opcodes.hrl"). 11 | 12 | %% @doc See docs/compact-encoding.rst 13 | %% The logic is based on beam_asm:encode_arg in the compiler app 14 | encode({x, X}, _Mod) -> beam_asm:encode(?tag_x, X); 15 | 16 | encode({y, Y}, _Mod) -> beam_asm:encode(?tag_y, Y); 17 | 18 | encode({f, F}, _Mod) -> beam_asm:encode(?tag_f, F); 19 | 20 | encode(nil, Mod) -> encode([], Mod); 21 | 22 | % Special value used in BIF opcode and others, to mark unused arg 23 | encode(ignore, Mod) -> encode([], Mod); 24 | 25 | encode([], _Mod) -> beam_asm:encode(?tag_a, 0); 26 | 27 | encode({atom, Atom}, Mod = #{'$' := module}) -> 28 | %% Assume atom already exists, will crash if it doesn't 29 | beam_asm:encode(?tag_a, index_of(Atom, atoms, Mod) + 1); 30 | 31 | encode({extfunc, Mod, Fun, Arity}, Mod0 = #{'$' := module}) -> 32 | ImportIndex = index_of({Mod, Fun, Arity}, imports, Mod0), 33 | beam_asm:encode(?tag_u, ImportIndex); 34 | 35 | encode({lambda, Label, NumFree}, Mod0 = #{'$' := module}) -> 36 | LambdaIndex = index_of({Label, NumFree}, lambdas, Mod0), 37 | beam_asm:encode(?tag_u, LambdaIndex); 38 | 39 | encode({jumptab, JTab}, Mod0 = #{'$' := module}) -> 40 | JTabIndex = index_of(JTab, jumptabs, Mod0), 41 | beam_asm:encode(?tag_u, JTabIndex); 42 | 43 | encode({literal, Lit}, Mod0 = #{'$' := module}) -> 44 | LitIndex = index_of(Lit, literals, Mod0), 45 | beam_asm:encode(?tag_u, LitIndex); 46 | 47 | encode(X, _Mod) when is_integer(X) -> beam_asm:encode(?tag_u, X); 48 | 49 | encode({integer, X}, _Mod) -> beam_asm:encode(?tag_u, X); 50 | 51 | encode(X, _Mod) -> 52 | ?COMPILE_ERROR("do not know how to encode ~p", [X]). 53 | 54 | 55 | index_of(Value, Key, Mod) -> 56 | case orddict:find(Value, maps:get(Key, Mod)) of 57 | {ok, X} -> X; 58 | error -> ?COMPILE_ERROR("value '~p' must be registered in module ~s", 59 | [Value, Key]) 60 | end. 61 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/src/uasm_stats.erl: -------------------------------------------------------------------------------- 1 | %%% @doc Statistics collection to give idea about value and opcode frequencies 2 | %%% in the processed code. 3 | %%% @end 4 | 5 | -module(uasm_stats). 6 | 7 | %% API 8 | -export([ 9 | count_opcode/1, 10 | count_value/1, 11 | dump/1, 12 | init/0 13 | ]). 14 | 15 | 16 | init() -> 17 | %% Collects stats per instruction (before they are diversified by arg size) 18 | ets:new(instr_stat, [public, named_table]), 19 | 20 | %% Collects stats per value type 21 | ets:new(value_stat, [public, named_table]). 22 | 23 | 24 | count_opcode(Identifier) when is_integer(Identifier) -> 25 | ets:update_counter(instr_stat, Identifier, 1, {Identifier, 0}). 26 | 27 | 28 | count_value(Identifier) when is_atom(Identifier) -> 29 | ets:update_counter(value_stat, Identifier, 1, {Identifier, 0}). 30 | 31 | 32 | dump(Filename) -> 33 | Dump = [ 34 | "Instruction stats:\n", 35 | dump_tab(instr_stat), 36 | "\n" 37 | "Data types stats:\n", 38 | dump_tab(value_stat) 39 | ], 40 | file:write_file(Filename, Dump). 41 | 42 | 43 | dump_tab(Tab) -> 44 | Pairs0 = ets:tab2list(Tab), 45 | Pairs1 = lists:keysort(2, Pairs0), 46 | lists:map(fun({K, V}) -> 47 | io_lib:format("~p\t~p\n", [K, V]) 48 | end, 49 | Pairs1). 50 | -------------------------------------------------------------------------------- /Compiler.old/apps/uasm/src/uasm_util.erl: -------------------------------------------------------------------------------- 1 | %%% @doc 2 | %%% @end 3 | 4 | -module(uasm_util). 5 | 6 | -include_lib("uerlc/include/uerlc.hrl"). 7 | 8 | %% API 9 | -export([assert_unsigned_fits/3, assert_signed_fits/3]). 10 | 11 | 12 | assert_unsigned_fits(Name, N, auto_bits) -> 13 | ok; % no check 14 | 15 | assert_unsigned_fits(Name, N, Bits) when is_integer(N), is_integer(Bits) -> 16 | Bin = <>, 17 | <> = Bin, 18 | case N == N1 of 19 | true -> ok; 20 | false -> ?COMPILE_ERROR("Value ~s does not fit into ~B bits", [Name, Bits]) 21 | end. 22 | 23 | 24 | assert_signed_fits(Name, N, Bits) -> 25 | Bin = <>, 26 | <> = Bin, 27 | case N == N1 of 28 | true -> ok; 29 | false -> ?COMPILE_ERROR("Value ~s does not fit into ~B bits", [Name, Bits]) 30 | end. 31 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | .idea 17 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/include/uerlc.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(UERLC_HRL__). 2 | -define(UERLC_HRL__, 1). 3 | 4 | 5 | %% When modifying regy and regx (maxarity) limits, please also modify bits in 6 | %% uasm_encode_int where these macros are used 7 | -define(LIMIT_MAX_REGY, 256). 8 | -define(LIMIT_MAXARITY, 256). 9 | 10 | -define(COMPILE_ERROR(Message), 11 | begin 12 | uerlc:error(header), 13 | io:format(?MODULE_STRING ++ ": ~s~n", [Message]), 14 | uerlc:error(footer), 15 | erlang:error(compile_error) 16 | end). 17 | 18 | -define(COMPILE_ERROR(Format, Args), 19 | begin 20 | uerlc:error(header), 21 | io:format(?MODULE_STRING ++ ": " ++ Format, Args), 22 | uerlc:error(footer), 23 | erlang:error(compile_error) 24 | end). 25 | 26 | -define(COLOR_TERM(Color, T), color:Color(io_lib:format("~p", [T]))). 27 | 28 | -define(COMPILE_ERROR1(Message, Term1), 29 | begin 30 | uerlc:error(header), 31 | io:format(?MODULE_STRING ++ ": ~s: ~s", 32 | [color:whiteb(Message), ?COLOR_TERM(red, Term1)]), 33 | uerlc:error(footer), 34 | erlang:error(compile_error) 35 | end). 36 | 37 | -define(ASSERT(Cond, Msg), case (Cond) of 38 | true -> ok; 39 | _ -> ?COMPILE_ERROR(Msg) 40 | end). 41 | 42 | -endif. % UERLC_HRL 43 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/src/uerlc.app.src: -------------------------------------------------------------------------------- 1 | {application, uerlc, 2 | [{description, "BEAM Assembly to MicroErlang bytecode compiler"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env, []}, 10 | {modules, []}, 11 | 12 | {maintainers, []}, 13 | {licenses, []}, 14 | {links, []} 15 | ]}. 16 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/src/uerlc.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc BEAM Assembly to MicroErlang compiler 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(uerlc). 7 | 8 | -behaviour(application). 9 | 10 | %% Application callbacks 11 | -export([ 12 | arguments_loop/1, 13 | debug/1, 14 | debug_write_term/2, 15 | error/1, 16 | start/0, 17 | start/2, 18 | stop/1, 19 | try_do/2, 20 | varint/1 21 | ]). 22 | 23 | -include_lib("uerlc/include/uerlc.hrl"). 24 | 25 | %%==================================================================== 26 | %% API 27 | %%==================================================================== 28 | 29 | start() -> 30 | start(normal, init:get_plain_arguments()). 31 | 32 | %% @doc Pass in a string which will be compiled 33 | debug(Filename) -> 34 | arguments_loop([Filename]). 35 | 36 | start(_StartType, Args) -> 37 | %% TODO: Asking for trouble here but hell is this fast 38 | put(e4_machine_word_bits, 64), 39 | 40 | arguments_loop(Args), 41 | init:stop(). 42 | 43 | stop(_State) -> 44 | ok. 45 | 46 | %%==================================================================== 47 | %% Internal functions 48 | %%==================================================================== 49 | 50 | arguments_loop([]) -> 51 | io:format("~s~n", [color:greenb("uERLC: All arguments were processed")]), 52 | ok; 53 | 54 | arguments_loop([InputPath | Tail]) -> 55 | io:format("uERLC: Processing: ~p...~n", [InputPath]), 56 | try 57 | compile(InputPath), 58 | arguments_loop(Tail) 59 | catch throw:compile_failed -> 60 | io:format("~s - ~s~n", [color:yellow("uERLC: Compilation failed"), 61 | InputPath]) 62 | end. 63 | 64 | 65 | try_do(What, Fun) -> 66 | try Fun() 67 | catch 68 | throw:compile_failed -> 69 | erlang:throw(compile_failed); % chain the error out 70 | T:Err -> 71 | io:format("~n~s (~s): ~s~n" 72 | "~s~n", 73 | [color:white("uERLC: Failed"), 74 | color:yellow(What), 75 | ?COLOR_TERM(redb, {T, Err}), 76 | ?COLOR_TERM(blackb, erlang:get_stacktrace()) 77 | ]), 78 | erlang:throw(compile_failed) 79 | end. 80 | 81 | 82 | compile(InputPath) -> 83 | %% Process Kernel to MicroErlang Assembler transformation 84 | Module = try_do("top-level uERLC invocation", 85 | fun() -> uerlc_compile:process(InputPath) end 86 | ), 87 | try_do("top-level uASM invocation", 88 | fun() -> uasm:process(InputPath, Module) end 89 | ). 90 | 91 | 92 | %% @doc Variable length unsigned int encoding, highest bit is set to 1 for every 93 | %% encoded 7+1 bit sequence, and is set to 0 in the last 7+1 bits 94 | varint(N) when N < 0 -> erlang:error("varint: n<0"); 95 | 96 | varint(N) when N =< 127 -> <<0:1, N:7>>; % last byte 97 | 98 | varint(N) -> 99 | Bytes = [<<0:1, (N rem 128):7>>, varint_with_bit(N bsr 7)], 100 | iolist_to_binary(lists:reverse(Bytes)). 101 | 102 | 103 | %% @doc Same as varint with high bit always set 104 | varint_with_bit(N) when N =< 127 -> <<1:1, N:7>>; % last byte 105 | 106 | varint_with_bit(N) -> 107 | Bytes = [<<1:1, (N rem 128):7>>, varint_with_bit(N bsr 7)], 108 | iolist_to_binary(lists:reverse(Bytes)). 109 | 110 | 111 | error(header) -> 112 | io:format("~n" 113 | "+--------------------------------------------------------~n" 114 | "| Error: "); 115 | 116 | error(footer) -> 117 | io:format("~n" 118 | "+--------------------------------------------------------"). 119 | 120 | 121 | debug_write_term(Filename, Term) -> 122 | file:write_file(Filename, 123 | erlang:iolist_to_binary( 124 | io_lib:format("~p", [Term]))). 125 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/src/uerlc_compile.erl: -------------------------------------------------------------------------------- 1 | -module(uerlc_compile). 2 | -export([process/1]). 3 | 4 | %% @doc Takes filename as input, produces compiled BEAM assembly AST and then 5 | %% passes are invoked to process it further 6 | -spec process(string()) -> #{}. 7 | process(F) -> 8 | case compile:file(F, ['S', binary, report]) of 9 | {ok, _ModuleName, BeamS} -> 10 | uerlc:try_do( 11 | "e4_pass_S - Erlang 'S' Assembly to E4 Assembly", 12 | fun() -> uerlc_pass_from_S:process(BeamS) end 13 | ); 14 | %% TODO optimize {label1} Code {label2} Code can be collapsed 15 | %% into {label1}{label} Code if the code is identical 16 | Error -> 17 | io:format("~n~s: ~p~n", [F, Error]) 18 | end. 19 | -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/src/uerlc_pass_from_S.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(uerlc_pass_from_S). 3 | 4 | %% API 5 | -export([process/1]). 6 | 7 | -include_lib("uerlc/include/uerlc.hrl"). 8 | -include_lib("uasm/include/uasm.hrl"). 9 | 10 | process({Module, _Exports, Attrs, Forms, _}) -> 11 | Mod0 = #{ 12 | '$' => module, 13 | mod => Module, 14 | funs => orddict:new(), 15 | exports => orddict:new(), 16 | attrs => Attrs 17 | }, 18 | 19 | process_forms(Forms, Mod0). 20 | 21 | %%%----------------------------------------------------------------------------- 22 | 23 | process_forms([], Mod) -> Mod; 24 | process_forms([{function, Name, Arity, _, Code0} | Forms], 25 | Mod0 = #{'$' := module, funs := Funs0}) -> 26 | #{'$' := pfun, code := Code1, mod := Mod1} = process_fun(Code0, Mod0, []), 27 | 28 | Fun = #{ 29 | '$' => e4fun, 30 | name => Name, 31 | arity => Arity, 32 | code => Code1 33 | }, 34 | Funs1 = orddict:store({Name, Arity}, Fun, Funs0), 35 | process_forms(Forms, Mod1#{funs => Funs1}); 36 | 37 | process_forms([X | _Forms], _Mod) -> 38 | ?COMPILE_ERROR("Unknown form ~p", [X]). 39 | 40 | 41 | process_fun([], Mod0, Out) -> 42 | #{'$' => pfun, 43 | code => lists:reverse(Out), 44 | mod => Mod0}; 45 | 46 | process_fun([{gc_bif, Name, Fail, Heap, Args, Result} | Tail], Mod0, Out) -> 47 | Cmd = #{ 48 | '$' => e4bif, 49 | args => Args, 50 | fail => Fail, 51 | gc => Heap, 52 | name => Name, 53 | result => Result 54 | }, 55 | process_fun(Tail, Mod0, [Cmd | Out]); 56 | 57 | process_fun([{bif, Name, Fail, Args, Result} | Tail], Mod0, Out) -> 58 | Cmd = #{ 59 | '$' => e4bif, 60 | args => Args, 61 | fail => Fail, 62 | name => Name, 63 | result => Result 64 | }, 65 | process_fun(Tail, Mod0, [Cmd | Out]); 66 | 67 | process_fun([{test, F, Fail, Args} | Tail], Mod0, Out) -> 68 | Cmd = #{ 69 | '$' => e4bif, 70 | args => Args, 71 | fail => Fail, 72 | name => F 73 | }, 74 | process_fun(Tail, Mod0, [Cmd | Out]); 75 | 76 | process_fun([{call, Arity, Label} | Tail], Mod0, Out) -> 77 | Cmd = #{ 78 | '$' => e4call, 79 | arity => Arity, 80 | target => Label 81 | }, 82 | process_fun(Tail, Mod0, [Cmd | Out]); 83 | 84 | process_fun([{call_last, Arity, Label, Dealloc} | Tail], Mod0, Out) -> 85 | Cmd = #{ 86 | '$' => e4call, 87 | arity => Arity, 88 | dealloc => Dealloc, 89 | tailcall => true, 90 | target => Label 91 | }, 92 | process_fun(Tail, Mod0, [Cmd | Out]); 93 | 94 | process_fun([{call_only, Arity, Label} | Tail], Mod0, Out) -> 95 | Cmd = #{ 96 | '$' => e4call, 97 | arity => Arity, 98 | tailcall => true, 99 | target => Label 100 | }, 101 | process_fun(Tail, Mod0, [Cmd | Out]); 102 | 103 | process_fun([{call_ext, Arity, {extfunc, M, F, Arity}} | Tail], Mod0, Out) -> 104 | Cmd = #{ 105 | '$' => e4call, 106 | arity => Arity, 107 | target => {extfunc, M, F, Arity} 108 | }, 109 | process_fun(Tail, Mod0, [Cmd | Out]); 110 | 111 | process_fun([{call_ext_only, Arity, {extfunc, M, F, Arity}} | Tail], 112 | Mod0, Out) -> 113 | Cmd = #{ 114 | '$' => e4call, 115 | arity => Arity, 116 | tailcall => true, 117 | target => {extfunc, M, F, Arity} 118 | }, 119 | process_fun(Tail, Mod0, [Cmd | Out]); 120 | 121 | process_fun([{call_ext_last, Arity, {extfunc, M, F, Arity}, De} | Tail], 122 | Mod0, Out) -> 123 | Cmd = #{ 124 | '$' => e4call, 125 | arity => Arity, 126 | dealloc => De, 127 | tailcall => true, 128 | target => {extfunc, M, F, Arity} 129 | }, 130 | process_fun(Tail, Mod0, [Cmd | Out]); 131 | 132 | process_fun([{deallocate, N}, return | Tail], Mod0, Out) -> 133 | Cmd = #{ 134 | '$' => e4ret, 135 | dealloc => N 136 | }, 137 | process_fun(Tail, Mod0, [Cmd | Out]); 138 | 139 | process_fun([return | Tail], Mod0, Out) -> 140 | Cmd = #{ 141 | '$' => e4ret, 142 | dealloc => 0 143 | }, 144 | process_fun(Tail, Mod0, [Cmd | Out]); 145 | 146 | process_fun([{line, _} | Tail], Mod0, Out) -> 147 | %% TODO: maybe debug info comes from here 148 | process_fun(Tail, Mod0, Out); 149 | 150 | %%process_fun([FunInfo = {func_info, {atom, _ModName}, {atom, Fun}, Arity}, 151 | %% Lbl = {label, Label} | Tail], Mod0, Out) -> 152 | %% Mod1 = mark_function_start(Fun, Arity, Label, Mod0), 153 | %% process_fun(Tail, Mod1, [FunInfo, Lbl | Out]); 154 | 155 | process_fun([Code | Tail], Mod0, Out) -> 156 | process_fun(Tail, Mod0, [Code | Out]). 157 | 158 | 159 | %%mark_function_start(Fun, Arity, Label, 160 | %% Mod0 = #{'$' := module, exports := Exports}) -> 161 | %% Exports1 = orddict:store({Fun, Arity}, Label, Exports), 162 | %% Mod0#{exports => Exports1}. -------------------------------------------------------------------------------- /Compiler.old/apps/uerlc/src/uerlc_print.erl: -------------------------------------------------------------------------------- 1 | %%% @doc Print the nice indented colored intermediate code 2 | %%% @end 3 | 4 | -module(uerlc_print). 5 | 6 | -include_lib("uerlc/include/uerlc.hrl"). 7 | 8 | %% API 9 | -export([format_ic/2]). 10 | 11 | format_ic(L, Indent) when is_list(L) -> 12 | [format_ic(Item, Indent) || Item <- L]; 13 | format_ic(C, Indent) -> 14 | io_lib:format("~s~s~n", [i(Indent), format_op(C)]). 15 | 16 | format_op(Other) -> 17 | ?COMPILE_ERROR("format_op: unknown ~p", [Other]). 18 | 19 | str(X) when is_atom(X) -> atom_to_list(X); 20 | str(X) when is_binary(X) -> io_lib:format("~s", [X]); 21 | str({A, B}) when is_atom(A), is_integer(B) -> 22 | io_lib:format("~s/~p", [A, B]); 23 | str(X) -> lists:flatten(io_lib:format("~p", [X])). 24 | 25 | i(I) when I =< 0 -> []; 26 | i(I) -> lists:duplicate((I-1) * 4, 32). 27 | -------------------------------------------------------------------------------- /Compiler.old/elvis.config: -------------------------------------------------------------------------------- 1 | [{ 2 | elvis, [ 3 | {config, 4 | [ 5 | #{dirs => ["src", "test"], 6 | filter => "*.erl", 7 | ruleset => erl_files, 8 | rules => [ 9 | {elvis_style, no_debug_call, disable} 10 | ]}, 11 | #{dirs => ["."], 12 | filter => "Makefile", 13 | ruleset => makefiles}, 14 | #{dirs => ["."], 15 | filter => "rebar.config", 16 | ruleset => rebar_config}, 17 | #{dirs => ["."], 18 | filter => "elvis.config", 19 | ruleset => elvis_config} 20 | ] 21 | } 22 | ] 23 | }]. -------------------------------------------------------------------------------- /Compiler.old/rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | debug_info, 3 | {i, "apps"} 4 | ]}. 5 | 6 | {deps, [ 7 | {color, {git, "https://github.com/julianduque/erlang-color.git", {ref, "master"}}} 8 | ]}. 9 | 10 | {profiles, [ 11 | {dev, [ 12 | {deps, [ 13 | {eper, {git, "https://github.com/massemanet/eper.git", {ref, "master"}}} 14 | ]} 15 | ]} 16 | ]}. 17 | -------------------------------------------------------------------------------- /Compiler.old/uerlc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | erl -pa _build/default/lib/*/ebin -noshell -s uerlc -extra $@ 3 | -------------------------------------------------------------------------------- /Compiler/.gitignore: -------------------------------------------------------------------------------- 1 | *.hi 2 | *.o 3 | .stack* 4 | cabal.config 5 | .idea/ 6 | .ideaHaskellLib/ 7 | out/ 8 | 9 | *.prof 10 | -------------------------------------------------------------------------------- /Compiler/LICENSE: -------------------------------------------------------------------------------- 1 | see LICENSE one level up -------------------------------------------------------------------------------- /Compiler/Makefile: -------------------------------------------------------------------------------- 1 | STACKOPT=--profile 2 | RUNOPT=+RTS -xc 3 | 4 | .PHONY: compile 5 | compile: 6 | stack build $(STACKOPT) 7 | 8 | TESTDATA=testData/ 9 | 10 | INP1=$(TESTDATA)/gb_trees.S 11 | INP2=$(TESTDATA)/mochijson.S 12 | INP3=$(TESTDATA)/mochijson2.S 13 | 14 | %.S: %.erl 15 | erlc -S -o $(@D) $^ 16 | 17 | .PHONY: run1 run2 run3 18 | run1: compile $(INP1) 19 | stack exec -- uerlc-exe $(INP1) $(RUNOPT) 2>&1 | less -r 20 | run2: compile $(INP2) 21 | stack exec -- uerlc-exe $(INP2) $(RUNOPT) 2>&1 | less -r 22 | run3: compile $(INP3) 23 | stack exec -- uerlc-exe $(INP3) $(RUNOPT) 2>&1 | less -r 24 | -------------------------------------------------------------------------------- /Compiler/README.rst: -------------------------------------------------------------------------------- 1 | Compiler 2 | ======== 3 | 4 | Cross-compiler tool which takes .S beam assembly files and writes out 5 | compressed binary MicroErlang modules. 6 | 7 | Compiling 8 | --------- 9 | 10 | * ``sudo apt install ghc haskell-stack`` 11 | * ``make compile`` or ``make run`` 12 | 13 | For comfortable editing: 14 | 15 | * IntelliJ IDEA + IntelliJ-Haskell plugin and configure path to stack in 16 | the IDEA SDK options 17 | * ``stack install hindent`` and ``stack install stylish-haskell`` and 18 | configure them in IDEA Options 19 | 20 | With these features configured, IDEA becomes quite Haskell-friendly. 21 | -------------------------------------------------------------------------------- /Compiler/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /Compiler/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import qualified Asm.Mod as AM 4 | import qualified BeamSParser as P0 5 | import qualified Bitcode.Encode.Huffman as Huff 6 | import qualified Bitcode.Mod as BM 7 | import qualified Pass.PassAsm as P2 8 | import qualified Pass.PassBeamS as P1 9 | import qualified Pass.PassBitcode as P3 10 | import qualified Term as T 11 | 12 | import Data.Word (Word8) 13 | import System.Environment 14 | import System.IO 15 | import System.Log.Handler.Syslog 16 | import System.Log.Logger 17 | 18 | -- Run chain of compile passes and print some intermediate results 19 | transpile :: String -> String -> IO BM.Module 20 | transpile _fileName input = do 21 | let s1 = P0.transform input -- pass 1 parse S file 22 | -- 23 | let s2 = P1.transform s1 -- pass 2 compile S to microErlang Assembly 24 | print s2 25 | -- 26 | let s3 = P2.transform s2 -- pass 3 encode args as bits and assign opcodes 27 | print s3 28 | -- 29 | let bitcode = P3.transform s3 -- pass 4 produce bitstream, compress opcodes 30 | return bitcode 31 | 32 | initLogging :: IO () 33 | initLogging = do 34 | s <- openlog "SyslogStuff" [PID] USER DEBUG 35 | updateGlobalLogger rootLoggerName (addHandler s) 36 | updateGlobalLogger "uerlc" (setLevel DEBUG) 37 | 38 | main :: IO () 39 | main = do 40 | initLogging 41 | [fileName] <- getArgs 42 | fh <- openFile fileName ReadMode 43 | contents <- hGetContents fh 44 | bm0 <- transpile fileName contents 45 | -- 46 | print bm0 47 | print $ BM.opStats bm0 48 | -- 49 | putStrLn "done" 50 | -------------------------------------------------------------------------------- /Compiler/src/Asm.hs: -------------------------------------------------------------------------------- 1 | module Asm 2 | ( allocate 3 | , badarg 4 | , badmatch 5 | , BinaryFlags(..) 6 | , bsContextToBin 7 | , bsInit 8 | , bsPutInteger 9 | , bsRestore 10 | , bsSave 11 | , BuiltinError(..) 12 | , callBif 13 | , callExt 14 | , callFun 15 | , callLabel 16 | , CallType(..) 17 | , caseClause 18 | , CodeLoc(..) 19 | , comment 20 | , cons 21 | , deallocate 22 | , decons 23 | , funcClause 24 | , ifClause 25 | , Instruction(..) 26 | , jump 27 | , JumpTab(..) 28 | , label 29 | , LabelLoc(..) 30 | , makeFun 31 | , move 32 | , ReadLoc(..) 33 | , ret 34 | , select 35 | , SelectSubj(..) 36 | , setNil 37 | , test 38 | , testHeap 39 | , trim 40 | , tupleGetEl 41 | , tupleNew 42 | , tuplePut 43 | , tupleSetEl 44 | , WriteLoc(..) 45 | ) where 46 | 47 | import Asm.Binary 48 | import Asm.Instruction 49 | import Asm.Locations 50 | import qualified Term as T 51 | 52 | jump :: LabelLoc -> Instruction 53 | jump = AJump 54 | 55 | label :: Int -> Instruction 56 | label i = ALabel (LabelLoc i) 57 | 58 | comment :: Show a => a -> Instruction 59 | comment x = AComment $ show x 60 | 61 | ret :: Int -> Instruction 62 | ret = ARet 63 | 64 | move :: ReadLoc -> WriteLoc -> Instruction 65 | move = AMove 66 | 67 | funcClause :: Instruction 68 | funcClause = AError EFunClause 69 | 70 | caseClause :: Instruction 71 | caseClause = AError ECaseClause 72 | 73 | ifClause :: Instruction 74 | ifClause = AError EIfClause 75 | 76 | badarg :: Instruction 77 | badarg = AError EBadArg 78 | 79 | badmatch :: ReadLoc -> Instruction 80 | badmatch r = AError (EBadMatch r) 81 | 82 | tupleNew :: Int -> WriteLoc -> Instruction 83 | tupleNew = ATupleNew 84 | 85 | tuplePut :: ReadLoc -> Instruction 86 | tuplePut = ATuplePut 87 | 88 | tupleGetEl :: ReadLoc -> ReadLoc -> WriteLoc -> Instruction 89 | tupleGetEl = ATupleGetEl 90 | 91 | tupleSetEl :: ReadLoc -> ReadLoc -> WriteLoc -> Instruction 92 | tupleSetEl = ATupleSetEl 93 | 94 | testHeap :: Int -> Int -> Instruction 95 | testHeap = ATestHeap 96 | 97 | allocate :: Int -> Int -> Instruction 98 | allocate = AAlloc 99 | 100 | deallocate :: Int -> Instruction 101 | deallocate = ADealloc 102 | 103 | test :: String -> LabelLoc -> [ReadLoc] -> Maybe Int -> WriteLoc -> Instruction 104 | test = ATest 105 | 106 | callLabel :: Int -> LabelLoc -> CallType -> Instruction 107 | callLabel arity lbl = ACall arity (CLabel lbl) 108 | 109 | callExt :: (String, String, Int) -> CallType -> Instruction 110 | callExt (m, f, arity) = ACall arity (CExtFunc m f arity) 111 | 112 | callBif :: 113 | String -> LabelLoc -> [ReadLoc] -> CallType -> WriteLoc -> Instruction 114 | callBif = ACallBif 115 | 116 | decons :: ReadLoc -> WriteLoc -> WriteLoc -> Instruction 117 | decons = ADecons 118 | 119 | select :: SelectSubj -> ReadLoc -> LabelLoc -> JumpTab -> Instruction 120 | select = ASelect 121 | 122 | cons :: ReadLoc -> ReadLoc -> WriteLoc -> Instruction 123 | cons = ACons 124 | 125 | callFun :: Int -> Instruction 126 | callFun = ACallFun 127 | 128 | setNil :: WriteLoc -> Instruction 129 | setNil = ASetNil 130 | 131 | trim :: Int -> Instruction 132 | trim = ATrim 133 | 134 | makeFun :: LabelLoc -> Int -> Instruction 135 | makeFun = AMakeFun 136 | 137 | bsContextToBin :: ReadLoc -> Instruction 138 | bsContextToBin = ABsContextToBin 139 | 140 | bsSave :: ReadLoc -> Int -> Instruction 141 | bsSave = ABsSave 142 | 143 | bsRestore :: ReadLoc -> Int -> Instruction 144 | bsRestore = ABsRestore 145 | 146 | bsInit :: Int -> Int -> WriteLoc -> LabelLoc -> Instruction 147 | bsInit = ABsInit 148 | 149 | bsPutInteger :: ReadLoc -> BinaryFlags -> WriteLoc -> Instruction 150 | bsPutInteger = ABsPutInteger 151 | -------------------------------------------------------------------------------- /Compiler/src/Asm/Binary.hs: -------------------------------------------------------------------------------- 1 | module Asm.Binary 2 | ( BinaryFlags(..) 3 | ) where 4 | 5 | data BinUnitWidth = 6 | BinUnitWidth Int 7 | Int 8 | 9 | -- options for binary value: (unitsize, signed, bigendian) 10 | data BinaryFlags = BinaryFlags 11 | { bfUnitSize :: Int 12 | , bfSigned :: Bool 13 | , bfBig :: Bool 14 | } 15 | 16 | instance Show BinaryFlags where 17 | show (BinaryFlags u sig big) = 18 | "unit:" ++ show u ++ "/" ++ sigStr ++ "/" ++ bigStr 19 | where 20 | sigStr = 21 | if sig 22 | then "signed" 23 | else "unsigned" 24 | bigStr = 25 | if big 26 | then "big" 27 | else "little" 28 | -------------------------------------------------------------------------------- /Compiler/src/Asm/Func.hs: -------------------------------------------------------------------------------- 1 | module Asm.Func 2 | ( Func(..) 3 | ) where 4 | 5 | import qualified Asm as A 6 | import qualified Asm.Instruction as AI 7 | import qualified Term as T 8 | 9 | import Data.List 10 | 11 | data Func = Func 12 | { name :: T.FunArity 13 | , code :: [AI.Instruction] 14 | } 15 | 16 | instance Show Func where 17 | show (Func (T.FunArity name arity) body) = 18 | intercalate "\n" ["", header, ops, footer, ""] 19 | where 20 | header = ";; fun " ++ funarity ++ " ------" 21 | footer = ";; ------ end " ++ funarity 22 | funarity = name ++ "/" ++ show arity 23 | indent2 t = " " ++ t 24 | ops = intercalate "\n" $ map (indent2 . show) body 25 | -------------------------------------------------------------------------------- /Compiler/src/Asm/Instruction.hs: -------------------------------------------------------------------------------- 1 | module Asm.Instruction 2 | ( SelectSubj(..) 3 | , Instruction(..) 4 | , CallType(..) 5 | , BuiltinError(..) 6 | , JumpTab(..) 7 | ) where 8 | 9 | import Asm.Binary 10 | import Asm.Locations 11 | import qualified Term as T 12 | 13 | data SelectSubj 14 | = SValue 15 | | STupleArity 16 | 17 | instance Show SelectSubj where 18 | show SValue = "-value" 19 | show STupleArity = "-tuple-arity" 20 | 21 | -- flags and options for a call 22 | data CallType 23 | = NormalCall 24 | | GcEnabledCall Int 25 | | TailCall 26 | | TailCallDealloc Int 27 | 28 | instance Show CallType where 29 | show NormalCall = "-normal" 30 | show TailCall = "-tail" 31 | show (GcEnabledCall n) = "-gc:" ++ show n 32 | show (TailCallDealloc n) = "-tail -dealloc:" ++ show n 33 | 34 | -- bad stuff (shorthand opcodes which cause exceptions) 35 | data BuiltinError 36 | = EBadArg 37 | | EBadMatch ReadLoc 38 | | ECaseClause 39 | | EFunClause 40 | | EIfClause 41 | 42 | instance Show BuiltinError where 43 | show EFunClause = "function_clause" 44 | show EIfClause = "if_clause" 45 | show ECaseClause = "case_clause" 46 | show EBadArg = "badarg" 47 | show (EBadMatch s) = "badmatch " ++ show s 48 | 49 | type JumpTab = [(T.Term, LabelLoc)] 50 | 51 | data Instruction 52 | = AAlloc Int 53 | Int 54 | -- convert matchstate in rxy to a (sub)binary 55 | | ABsContextToBin ReadLoc 56 | -- alloc arg0 words, arg1 live for gc, store result into writelocs 57 | | ABsInit Int 58 | Int 59 | WriteLoc 60 | LabelLoc 61 | -- store integer with unit/width/flags into writeloc 62 | | ABsPutInteger ReadLoc 63 | BinaryFlags 64 | WriteLoc 65 | | ABsRestore ReadLoc 66 | Int 67 | -- save match offset into save array[i] 68 | | ABsSave ReadLoc 69 | Int 70 | | ACallBif String 71 | LabelLoc 72 | [ReadLoc] 73 | CallType 74 | WriteLoc 75 | | ACall Int 76 | CodeLoc 77 | CallType 78 | | ACallFun Int 79 | | AComment String 80 | | ACons ReadLoc 81 | ReadLoc 82 | WriteLoc 83 | | ADealloc Int 84 | | ADecons ReadLoc 85 | WriteLoc 86 | WriteLoc 87 | | AError BuiltinError 88 | | AJump LabelLoc 89 | | ALabel LabelLoc 90 | | ALine Int 91 | | AMakeFun LabelLoc 92 | Int 93 | | AMove ReadLoc 94 | WriteLoc 95 | | ARet Int 96 | -- From a jumptable select a pair {value,label} and jump there, else jump to 97 | -- the onfail location 98 | | ASelect SelectSubj 99 | ReadLoc 100 | LabelLoc 101 | JumpTab 102 | | ASetNil WriteLoc 103 | | ATest String 104 | LabelLoc 105 | [ReadLoc] 106 | (Maybe Int) 107 | WriteLoc 108 | | ATestHeap Int 109 | Int 110 | | ATrim Int 111 | | ATupleGetEl ReadLoc 112 | ReadLoc 113 | WriteLoc 114 | | ATupleSetEl ReadLoc 115 | ReadLoc 116 | WriteLoc 117 | | ATupleNew Int 118 | WriteLoc 119 | | ATuplePut ReadLoc 120 | 121 | show1 :: String -> [String] -> String 122 | show1 s args = s ++ " " ++ unwords args 123 | 124 | instance Show Instruction where 125 | show (AAlloc a b) = show1 "alloc" [show a, show b] 126 | show (ABsContextToBin a) = show1 "bsctx2bin" [show a] 127 | show (ABsInit a b c d) = show1 "bsinit" [show a, show b, show c, show d] 128 | show (ABsPutInteger a b c) = show1 "bsputi" [show a, show b, show c] 129 | show (ABsRestore a b) = show1 "bsrest" [show a, show b] 130 | show (ABsSave a b) = show1 "bssave" [show a, show b] 131 | show (ACallBif a b c d e) = 132 | show1 "callbif" [show a, show b, show c, show d, show e] 133 | show (ACall a b c) = show1 "call" [show a, show b, show c] 134 | show (ACallFun a) = show1 "callf" [show a] 135 | show (AComment a) = show1 ";" [show a] 136 | show (ACons a b c) = show1 "cons" [show a, show b, show c] 137 | show (ADealloc a) = show1 "dealloc" [show a] 138 | show (ADecons a b c) = show1 "decons" [show a, show b, show c] 139 | show (AError a) = show1 "err" [show a] 140 | show (AJump a) = show1 "jmp" [show a] 141 | show (ALabel a) = show1 "; label" [show a] 142 | show (ALine a) = show1 "; line" [show a] 143 | show (AMakeFun a b) = show1 "mkfun" [show a, show b] 144 | show (AMove a b) = show1 "move" [show a, show b] 145 | show (ARet a) = show1 "ret" [show a] 146 | show (ASelect a b c d) = show1 "select" [show a, show b, show c, show d] 147 | show (ASetNil a) = show1 "nil" [show a] 148 | show (ATest a b c d e) = show1 "test" [show a, show b, show c, show d, show e] 149 | show (ATestHeap a b) = show1 "testheap" [show a, show b] 150 | show (ATrim a) = show1 "trim" [show a] 151 | show (ATupleGetEl a b c) = show1 "getel" [show a, show b, show c] 152 | show (ATupleSetEl a b c) = show1 "setel" [show a, show b, show c] 153 | show (ATupleNew a b) = show1 "mktuple" [show a, show b] 154 | show (ATuplePut a) = show1 "put" [show a] 155 | -------------------------------------------------------------------------------- /Compiler/src/Asm/Locations.hs: -------------------------------------------------------------------------------- 1 | module Asm.Locations 2 | ( LabelLoc(..) 3 | , CodeLoc(..) 4 | , ReadLoc(..) 5 | , WriteLoc(..) 6 | ) where 7 | 8 | import Asm.Binary 9 | import qualified Term as T 10 | 11 | data LabelLoc 12 | = LabelLoc Int 13 | | NoLabel 14 | 15 | instance Show LabelLoc where 16 | show (LabelLoc i) = "-label:" ++ show i 17 | show NoLabel = "-no-label" 18 | 19 | -- sources of the data (registers, literals, immediates...) 20 | data ReadLoc 21 | = RRegX Int 22 | | RRegY Int 23 | | RAtom String 24 | | RInt Integer 25 | | RLit T.Term 26 | | RNil 27 | | RBinaryFlags BinaryFlags 28 | 29 | rarrow :: String 30 | rarrow = "➚" 31 | 32 | warrow :: String 33 | warrow = "➘" 34 | 35 | instance Show ReadLoc where 36 | show (RRegX i) = "x" ++ show i ++ rarrow 37 | show (RRegY i) = "y" ++ show i ++ rarrow 38 | show (RAtom a) = show a 39 | show (RInt i) = show i 40 | show (RLit lit) = "lit:" ++ show lit 41 | show RNil = "[]" 42 | show (RBinaryFlags bf) = "binflags:" ++ show bf 43 | 44 | -- where you can possibly store the value 45 | data WriteLoc 46 | = WRegX Int 47 | | WRegY Int 48 | | WIgnore 49 | 50 | instance Show WriteLoc where 51 | show (WRegX i) = warrow ++ "x" ++ show i 52 | show (WRegY i) = warrow ++ "y" ++ show i 53 | show WIgnore = warrow ++ "drop" 54 | 55 | data CodeLoc 56 | = CLabel LabelLoc 57 | | CExtFunc String 58 | String 59 | Int 60 | 61 | instance Show CodeLoc where 62 | show (CLabel ulbl) = show ulbl 63 | show (CExtFunc m f a) = "@" ++ m ++ ":" ++ f ++ "/" ++ show a 64 | -------------------------------------------------------------------------------- /Compiler/src/Asm/Mod.hs: -------------------------------------------------------------------------------- 1 | module Asm.Mod 2 | ( Module(..) 3 | ) where 4 | 5 | import qualified Asm.Func as AF 6 | import qualified Term as T 7 | 8 | import Data.List 9 | import qualified Data.Map as Map 10 | 11 | data Module = Module 12 | { name :: String 13 | , funs :: Map.Map T.FunArity AF.Func 14 | , exports :: [T.FunArity] 15 | } 16 | 17 | instance Show Module where 18 | show (Module name' funs' _exports) = intercalate "\n" [header, funs, footer] 19 | where 20 | header = ";; uAsm module " ++ name' ++ "======" 21 | footer = ";; ====== end uAsm module " ++ name' 22 | funs = intercalate "\n" strFuns 23 | strFuns = map show (Map.elems funs') 24 | -------------------------------------------------------------------------------- /Compiler/src/BeamSParser.hs: -------------------------------------------------------------------------------- 1 | module BeamSParser 2 | ( transform 3 | ) where 4 | 5 | import Term 6 | import Uerlc 7 | 8 | import Control.Monad 9 | import System.IO 10 | import Text.ParserCombinators.Parsec 11 | import Text.ParserCombinators.Parsec.Expr 12 | import Text.ParserCombinators.Parsec.Language 13 | import qualified Text.ParserCombinators.Parsec.Token as Token 14 | 15 | languageDef = 16 | emptyDef 17 | { Token.commentLine = "%" 18 | , Token.commentStart = "" 19 | , Token.commentEnd = "" 20 | , Token.identStart = letter 21 | , Token.identLetter = alphaNum <|> char '_' 22 | , Token.reservedNames = [] 23 | , Token.reservedOpNames = [] 24 | , Token.opStart = unexpected "operation not allowed" 25 | } 26 | 27 | lexer = Token.makeTokenParser languageDef 28 | 29 | beamSParser :: Parser Term 30 | beamSParser = whiteSpace >> sequenceOfExprs 31 | 32 | sequenceOfExprs :: Parser Term 33 | sequenceOfExprs = do 34 | forms <- many erlTerm 35 | return $ ErlList forms 36 | 37 | erlComment :: Parser Term 38 | erlComment = do 39 | _ <- string "%" 40 | c <- manyTill anyChar newline 41 | return $ ErlComment c 42 | 43 | erlTerm :: Parser Term 44 | erlTerm = do 45 | expr <- erlExpr 46 | _ <- char '.' 47 | whiteSpace 48 | optional erlComment 49 | return expr 50 | 51 | erlExpr :: Parser Term 52 | erlExpr = 53 | erlTuple <|> erlList <|> erlAtom <|> erlInteger <|> erlString <|> erlBinary 54 | 55 | erlBinary :: Parser Term 56 | erlBinary = do 57 | _ <- string "<<" 58 | s <- stringLiteral 59 | _ <- string ">>" 60 | return $ BinaryStr s 61 | 62 | erlTuple :: Parser Term 63 | erlTuple = braces erlTupleContent 64 | 65 | erlTupleContent :: Parser Term 66 | erlTupleContent = do 67 | items <- commaSep erlExpr 68 | return $ ErlTuple items 69 | 70 | erlList :: Parser Term 71 | erlList = brackets erlListContent 72 | 73 | erlListContent :: Parser Term 74 | erlListContent = do 75 | items <- commaSep erlExpr 76 | return $ ErlList items 77 | 78 | erlInteger :: Parser Term 79 | erlInteger = do 80 | val <- integer 81 | return $ ErlInt val 82 | 83 | erlAtom :: Parser Term 84 | erlAtom = erlAtomStr <|> erlAtomQuoted 85 | 86 | erlAtomStr :: Parser Term 87 | erlAtomStr = do 88 | s <- identifier 89 | return $ Atom s 90 | 91 | erlAtomQuoted :: Parser Term 92 | erlAtomQuoted = do 93 | whiteSpace 94 | _ <- char '\'' 95 | s <- many (noneOf "'") 96 | _ <- char '\'' 97 | return $ Atom s 98 | 99 | erlString :: Parser Term 100 | erlString = do 101 | s <- stringLiteral 102 | return $ ErlStr s 103 | 104 | identifier = Token.identifier lexer 105 | 106 | braces = Token.braces lexer 107 | 108 | brackets = Token.brackets lexer 109 | 110 | commaSep = Token.commaSep lexer 111 | 112 | integer = Token.integer lexer 113 | 114 | whiteSpace = Token.whiteSpace lexer 115 | 116 | dot = Token.dot lexer 117 | 118 | stringLiteral = Token.stringLiteral lexer 119 | 120 | charLiteral = Token.charLiteral lexer 121 | 122 | transform :: String -> Term 123 | transform contents = 124 | case parse BeamSParser.beamSParser "" contents of 125 | Left parsecError -> Uerlc.err $ show parsecError 126 | Right expr -> expr 127 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Encode.hs: -------------------------------------------------------------------------------- 1 | module Bitcode.Encode 2 | ( encBinaryFlags 3 | , encBool 4 | , encCodeLocM 5 | , encJtabM 6 | , encLabelLoc 7 | , encLitM 8 | , encReadLocM 9 | , encSint 10 | , encUint 11 | , encWriteLoc 12 | ) where 13 | 14 | import qualified Asm as A 15 | import qualified Bits as B 16 | import Bitcode.Encode.Const 17 | import qualified Bitcode.Mod as BM 18 | import qualified Term as T 19 | 20 | import qualified Control.Monad.State as S 21 | import qualified Data.Bits as DBits 22 | 23 | -- Produce untagged unsigned integer with 2 bits size prefix (4-8-16-32 bits) 24 | encUint :: Int -> B.BitsList 25 | encUint u 26 | | u >= 0 && u < varlengthLimit0 = [B.bitsUB 0 2, B.bitsUB u varlength0] 27 | | u >= 0 && u < varlengthLimit1 = [B.bitsUB 1 2, B.bitsUB u varlength1] 28 | | u >= 0 && u < varlengthLimit2 = [B.bitsUB 2 2, B.bitsUB u varlength2] 29 | | u >= 0 && u < varlengthLimit3 = [B.bitsUB 3 2, B.bitsUB u varlength3] 30 | 31 | -- Produce untagged signed integer with 2 bits size prefix (4-8-16-32 bits) 32 | encSint :: Int -> B.BitsList 33 | encSint s 34 | | B.signedFitsIn s varlength0 = [B.bitsUB 0 2, B.bitsSB s varlength0] 35 | | B.signedFitsIn s varlength1 = [B.bitsUB 1 2, B.bitsSB s varlength1] 36 | | B.signedFitsIn s varlength2 = [B.bitsUB 2 2, B.bitsSB s varlength2] 37 | | B.signedFitsIn s varlength3 = [B.bitsUB 3 2, B.bitsSB s varlength3] 38 | 39 | encReadLocM :: A.ReadLoc -> BM.ModuleState B.BitsList 40 | encReadLocM (A.RRegX x) = return $ termTag termTagRegX : encUint x 41 | encReadLocM (A.RRegY y) = return $ termTag termTagRegY : encUint y 42 | encReadLocM A.RNil = return [termTag termTagNil] 43 | encReadLocM (A.RInt i) = do 44 | let limI = fromIntegral i -- todo bigint support? 45 | return $ termTag termTagInteger : encSint limI 46 | encReadLocM (A.RAtom a) = do 47 | aIndex <- BM.findAddAtomM a 48 | return $ termTag termTagAtom : encUint aIndex 49 | encReadLocM (A.RLit lit) = encLitM lit 50 | encReadLocM (A.RBinaryFlags bf) = return $ encBinaryFlags bf 51 | 52 | encBinaryFlags :: A.BinaryFlags -> B.BitsList 53 | encBinaryFlags (A.BinaryFlags unit sig big) = termTag termTagInteger : val 54 | where 55 | val = encUint $ (unit `DBits.shiftL` 2) + sigBit + bigBit 56 | sigBit = 57 | if sig 58 | then 2 59 | else 0 60 | bigBit = 61 | if big 62 | then 1 63 | else 0 64 | 65 | -- Update literal table if needed. Return index in the literal table 66 | encLitM :: T.Term -> BM.ModuleState [B.Bits] 67 | encLitM lit = do 68 | litIndex <- BM.findAddLitM lit 69 | return $ termTag termTagLiteral : encUint litIndex 70 | 71 | encWriteLoc :: A.WriteLoc -> B.BitsList 72 | encWriteLoc (A.WRegX x) = termTag termTagRegX : encUint x 73 | encWriteLoc (A.WRegY y) = termTag termTagRegY : encUint y 74 | encWriteLoc A.WIgnore = [termTag termTagNil] 75 | 76 | -- Encode code location as label, no label or an import (updates 77 | -- import table in the module if needed) 78 | encCodeLocM :: A.CodeLoc -> BM.ModuleState B.BitsList 79 | encCodeLocM (A.CLabel lloc) = return $ encLabelLoc lloc 80 | encCodeLocM (A.CExtFunc m f a) = encLitM lit -- do as import? 81 | where 82 | lit = T.ErlTuple [T.Atom m, T.Atom f, T.ErlInt (toInteger a)] 83 | 84 | encLabelLoc :: A.LabelLoc -> B.BitsList 85 | encLabelLoc (A.LabelLoc i) = encUint i 86 | encLabelLoc A.NoLabel = [termTag termTagNil] 87 | 88 | encBool :: Bool -> B.Bits 89 | encBool True = B.bitsUB 1 1 90 | encBool False = B.bitsUB 0 1 91 | 92 | encJtabM :: A.JumpTab -> BM.ModuleState B.BitsList 93 | encJtabM jtab = do 94 | jtIndex <- BM.addJumptabM jtab 95 | return $ encUint jtIndex 96 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Encode/Const.hs: -------------------------------------------------------------------------------- 1 | module Bitcode.Encode.Const where 2 | 3 | import qualified Bits as B 4 | 5 | import Data.Bits 6 | 7 | varlength0 :: Int 8 | varlength0 = 4 :: Int 9 | 10 | varlength1 :: Int 11 | varlength1 = 8 :: Int 12 | 13 | varlength2 :: Int 14 | varlength2 = 16 :: Int 15 | 16 | varlength3 :: Int 17 | varlength3 = 32 :: Int 18 | 19 | varlengthLimit0 :: Int 20 | varlengthLimit0 = 1 `shiftL` varlength0 21 | 22 | varlengthLimit1 :: Int 23 | varlengthLimit1 = 1 `shiftL` varlength1 24 | 25 | varlengthLimit2 :: Int 26 | varlengthLimit2 = 1 `shiftL` varlength2 27 | 28 | varlengthLimit3 :: Int 29 | varlengthLimit3 = 1 `shiftL` varlength3 30 | 31 | termTag_bitSize :: Int 32 | termTag_bitSize = 3 :: Int 33 | 34 | termTagRegX :: Int 35 | termTagRegX = 0 :: Int 36 | 37 | termTagRegY :: Int 38 | termTagRegY = 1 :: Int 39 | 40 | termTagNil :: Int 41 | termTagNil = 2 :: Int 42 | 43 | termTagAtom :: Int 44 | termTagAtom = 3 :: Int 45 | 46 | --termTagImport :: Int 47 | --termTagImport = 4 :: Int 48 | termTagBinaryFlags :: Int 49 | termTagBinaryFlags = 4 :: Int 50 | 51 | --termTagLambda :: Int 52 | --termTagLambda = 5 :: Int 53 | termTagLiteral :: Int 54 | termTagLiteral = 6 :: Int 55 | 56 | termTagInteger :: Int 57 | termTagInteger = 7 :: Int 58 | 59 | termTag :: Int -> B.Bits 60 | termTag t = B.bitsUB t termTag_bitSize 61 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Encode/Huffman.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExplicitForAll #-} 2 | 3 | -- Implementation comes from a Github Gist by ibtaylor/Huffman.hs 4 | -- https://gist.github.com/ibtaylor/1024266 5 | -- 6 | module Bitcode.Encode.Huffman 7 | ( makeEncoderFromImport 8 | , makeEncoderFromFreq 9 | , encodeSome 10 | , makeFreqTable 11 | , Encoder 12 | , Frequency(..) 13 | ) where 14 | 15 | import qualified Bits as B 16 | 17 | import qualified Data.List as L 18 | import qualified Data.Map as M 19 | import Data.Maybe 20 | import Data.Ord 21 | 22 | -- val contains either leaf value or tree children. Bits is bit length 23 | data HuffmanTree a 24 | = LeafNode a 25 | Int 26 | | InternalNode Int 27 | (HuffmanTree a) 28 | (HuffmanTree a) 29 | deriving (Eq) 30 | 31 | instance Show a => Show (HuffmanTree a) where 32 | show = go "" 33 | where 34 | spaces = map (const ' ') 35 | go _accum (LeafNode val nBits) = 36 | "--b:" ++ show nBits ++ ",val:" ++ show val ++ "\n" 37 | go accum (InternalNode nBits left right) = 38 | let root = "--b:" ++ show nBits ++ "-+" 39 | ss' = accum ++ tail (spaces root) 40 | lbranch = go (ss' ++ "|") left 41 | rbranch = go (ss' ++ " ") right 42 | in root ++ lbranch ++ ss' ++ "|\n" ++ ss' ++ "`" ++ rbranch 43 | 44 | frequency :: HuffmanTree a -> Int 45 | frequency (LeafNode _a x) = x 46 | frequency (InternalNode x _l _r) = x 47 | 48 | --type HDict = M.Map Int B.Bits 49 | data Encoder a = Encoder 50 | { eCodes :: M.Map a [Bool] 51 | , eTree :: HuffmanTree a 52 | } deriving (Show) 53 | 54 | data Frequency a = 55 | Frequency a 56 | Int 57 | deriving (Eq) 58 | 59 | instance Show a => Show (Frequency a) where 60 | show (Frequency val freq) = show val ++ " (x" ++ show freq ++ ")" 61 | 62 | instance Eq a => Ord (Frequency a) where 63 | compare (Frequency _val1 freq1) (Frequency _val2 freq2) = compare freq1 freq2 64 | 65 | --makeFrequency :: a -> Int -> (a, Int) 66 | --makeFrequency val freq = Frequency val freq 67 | makeEncoderFromImport :: 68 | Ord a 69 | => Eq a => 70 | [a] -> Encoder a 71 | makeEncoderFromImport input = Encoder {eTree = tree, eCodes = codes} 72 | where 73 | tree = makeTree $ makeFreqTable input 74 | codes = makeCodes tree 75 | 76 | makeEncoderFromFreq :: Ord a => [Frequency a] -> Encoder a 77 | makeEncoderFromFreq freq = Encoder {eTree = tree, eCodes = codes} 78 | where 79 | tree = makeTree freq 80 | codes = makeCodes tree 81 | 82 | -- traverse the huffman tree generating a map from the symbol to its huffman 83 | -- tree path (where False is left, and True is right) 84 | makeCodes :: Ord a => HuffmanTree a -> M.Map a [Bool] 85 | makeCodes = M.fromList . go [] 86 | -- leaf nodes mark the end of a path to a symbol 87 | where 88 | go p (LeafNode s _) = [(s, reverse p)] 89 | -- traverse both branches and accumulate a reverse path 90 | go p (InternalNode _ l r) = go (True : p) l ++ go (False : p) r 91 | 92 | -- from a table mapping symbols to their corresponding huffman tree bit paths, 93 | -- replace each instance of a symbol with its bit path 94 | encodeSome :: Ord a => Encoder a -> [a] -> B.Bits 95 | encodeSome Encoder {eCodes = tbl} = B.makeBits . concatMap get 96 | where 97 | get x = fromJust (M.lookup x tbl) 98 | 99 | makeFreqTable :: 100 | Ord a 101 | => Eq a => 102 | [a] -> [Frequency a] 103 | makeFreqTable input = makeFreqTable' (L.sort input) [] 104 | 105 | -- Group list contents by value, and collect counts for each value in 'accum' 106 | makeFreqTable' :: Eq a => [a] -> [Frequency a] -> [Frequency a] 107 | makeFreqTable' [] accum = accum 108 | makeFreqTable' (h:tl) accum = 109 | let (block, moreBlocks) = L.partition (h ==) tl 110 | newItem = Frequency h (1 + length block) 111 | in makeFreqTable' moreBlocks (newItem : accum) 112 | 113 | makeTree :: [Frequency a] -> HuffmanTree a 114 | makeTree inp 115 | -- First convert each freq tuple into a leaf then combine 116 | = combine inp2 117 | -- Repeatedly combine lowest freq trees and reinsert the result into 118 | -- the freq ordered list (todo: priority queue) 119 | where 120 | inp2 = map toLeaf inp 121 | combine [t] = t 122 | combine (ta:tb:ts) = 123 | combine . L.insertBy (comparing frequency) (merge ta tb) $ ts 124 | -- make an internal node from 2 trees. The freq is sum of two trees freq 125 | merge ta tb = InternalNode (frequency ta + frequency tb) ta tb 126 | -- make a leaf from (symbol,freq) 127 | toLeaf (Frequency val freq) = LeafNode val freq 128 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Func.hs: -------------------------------------------------------------------------------- 1 | module Bitcode.Func 2 | ( Func(..) 3 | ) where 4 | 5 | import qualified Bitcode.Op as BO 6 | import Data.List 7 | import qualified Term as T 8 | 9 | data Func = Func 10 | { bcfName :: T.FunArity 11 | , bcfCode :: [BO.Instruction] 12 | } 13 | 14 | instance Show Func where 15 | show (Func (T.FunArity name arity) body) = 16 | intercalate "\n" ["", header, ops, footer, ""] 17 | where 18 | header = ";; bitcode fun " ++ funarity ++ " ------" 19 | footer = ";; ------ end bitcode " ++ funarity 20 | funarity = name ++ "/" ++ show arity 21 | indent2 t = " " ++ t 22 | ops = intercalate "\n" $ map (indent2 . show) body 23 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Mod.hs: -------------------------------------------------------------------------------- 1 | module Bitcode.Mod 2 | ( Module(..) 3 | , ModuleState 4 | , new 5 | , findAtom 6 | , addAtom 7 | , findAddAtomM 8 | , findLit 9 | , findAddLitM 10 | , addLit 11 | , findImport 12 | , addImport 13 | , findAddImport 14 | , addJumptabM 15 | , profileOpcodeM 16 | ) where 17 | 18 | import qualified Asm as A 19 | import qualified Asm.Instruction as AI 20 | import qualified Bitcode.Encode.Huffman as H 21 | import qualified Bitcode.Func as BF 22 | import qualified Bitcode.Op as BO 23 | import qualified Term as T 24 | 25 | import qualified Control.Monad.State as S 26 | import Data.List 27 | import qualified Data.Map as Map 28 | import Data.Word (Word8) 29 | import qualified Debug.Trace as Dbg 30 | 31 | data Module = Module 32 | { name :: String 33 | -- all used atoms, in a table 34 | , atoms :: Map.Map String Int 35 | -- all mentioned literals in a table 36 | , literals :: Map.Map T.Term Int 37 | -- external mfarities referred from the code, in a table 38 | , imports :: Map.Map T.MFArity Int 39 | -- collection of funs each with its own bitcode 40 | , funs :: Map.Map T.FunArity BF.Func 41 | -- jump tables for selectVal and selectTupleArity 42 | , jTabs :: [AI.JumpTab] 43 | -- Encoder which will be created after compiling finished from opcode 44 | -- frequencies and will be used to encode opcodes into smaller bit counts 45 | , huffmanEncoder :: Maybe (H.Encoder Word8) 46 | -- Opcodes frequencies calculated (enable this manually to generate data for 47 | -- B.Op.hardcodedFreq) 48 | , opStats :: Map.Map BO.Opcode Int 49 | } 50 | 51 | type ModuleState = S.State Module 52 | 53 | new :: Module 54 | new = 55 | Module { name = "" 56 | , atoms = Map.empty 57 | , literals = Map.empty 58 | , imports = Map.empty 59 | , funs = Map.empty 60 | , jTabs = [] 61 | , huffmanEncoder = Nothing 62 | , opStats = Map.empty 63 | } 64 | 65 | instance Show Module where 66 | show m = intercalate "\n" [header, funsText, footer] 67 | where 68 | name' = name m 69 | funs' = funs m 70 | header = ";; bitcode module " ++ name' ++ "======" 71 | footer = ";; ====== end bitcode module " ++ name' 72 | funsText = intercalate "\n" strFuns 73 | strFuns = map show (Map.elems funs') 74 | 75 | -- Pure find atom function 76 | findAtom :: Module -> String -> Maybe Int 77 | findAtom m a = Map.lookup a (atoms m) 78 | 79 | -- Find atom and possibly add it 80 | findAddAtomM :: String -> S.State Module Int 81 | findAddAtomM a = do 82 | m <- S.get 83 | let lookupResult = Map.lookup a (atoms m) 84 | case lookupResult of 85 | Just i -> return i 86 | Nothing -> do 87 | let (m1, newI) = addAtom m a 88 | S.put m1 89 | return newI 90 | 91 | -- Pure add atom function 92 | addAtom :: Module -> String -> (Module, Int) 93 | addAtom m a = (m1, counter) 94 | where 95 | oldAtoms = atoms m 96 | counter = Map.size oldAtoms 97 | newAtoms = Map.insert a counter oldAtoms 98 | m1 = m {atoms = newAtoms} 99 | 100 | -- Pure lookup function 101 | findLit :: Module -> T.Term -> Maybe Int 102 | findLit m lit = Map.lookup lit (literals m) 103 | 104 | -- Pure add literal function 105 | addLit :: Module -> T.Term -> (Module, Int) 106 | addLit m lit = (m1, counter) 107 | where 108 | oldLiterals = literals m 109 | counter = Map.size oldLiterals 110 | newLiterals = Map.insert lit counter oldLiterals 111 | m1 = m {literals = newLiterals} 112 | 113 | -- Find and possibly add literal 114 | findAddLitM :: T.Term -> S.State Module Int 115 | findAddLitM lit = do 116 | m <- S.get 117 | let lookupResult = Map.lookup lit (literals m) 118 | case lookupResult of 119 | Just i -> return i 120 | Nothing -> do 121 | let (m1, newI) = addLit m lit 122 | S.put m1 123 | return newI 124 | 125 | -- Pure add Import function 126 | addImport :: Module -> T.MFArity -> (Module, Int) 127 | addImport m imp = (m1, counter) 128 | where 129 | oldImports = imports m 130 | counter = Map.size oldImports 131 | newImports = Map.insert imp counter oldImports 132 | m1 = m {imports = newImports} 133 | 134 | -- Pure find import function 135 | findImport :: Module -> T.MFArity -> Maybe Int 136 | findImport m imp = Map.lookup imp (imports m) 137 | 138 | -- Find and possibly add import 139 | findAddImport :: T.MFArity -> S.State Module Int 140 | findAddImport imp = do 141 | m <- S.get 142 | let lookupResult = Map.lookup imp (imports m) 143 | case lookupResult of 144 | Just i -> return i 145 | Nothing -> do 146 | let (m1, newI) = addImport m imp 147 | S.put m1 148 | return newI 149 | 150 | addJumptabM :: AI.JumpTab -> S.State Module Int 151 | addJumptabM jtab = do 152 | m <- S.get 153 | let oldJtabs = jTabs m 154 | index = length oldJtabs 155 | m1 = m {jTabs = oldJtabs ++ [jtab]} 156 | S.put m1 157 | return index 158 | 159 | profileOpcodeM :: BO.Opcode -> S.State Module () 160 | profileOpcodeM op = do 161 | m0 <- S.get 162 | let s0 = opStats m0 163 | s1 = Map.insertWith (+) op 1 s0 164 | S.put $ m0 {opStats = s1} 165 | return () 166 | 167 | --profileOpcodeM :: BO.Opcode -> ModuleState () 168 | --profileOpcodeM op = do 169 | -- m0 <- S.get 170 | -- let m1 = profileOpcode m0 op 171 | -- S.put m1 172 | -- return () 173 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/Op.hs: -------------------------------------------------------------------------------- 1 | module Bitcode.Op 2 | ( Opcode(..) 3 | , Instruction 4 | , makeInstruction 5 | , harcodedFrequencies 6 | ) where 7 | 8 | import qualified Bits as B 9 | import Bitcode.Encode.Huffman as H 10 | import Uerlc 11 | 12 | import qualified Data.List as L 13 | import Data.Maybe (fromJust) 14 | import Data.Tuple 15 | import Data.Word (Word8) 16 | 17 | data Opcode 18 | = Alloc 19 | | BsContextToBin 20 | | BsInit 21 | | BsPutInteger 22 | | BsSave 23 | | BsRestore 24 | | Call 25 | | CallBif 26 | | CallBifGc 27 | | CallFun 28 | | CallGc 29 | | CallTail 30 | | CallTailDealloc 31 | | Cons 32 | | Decons 33 | | Error 34 | | Jump 35 | | MakeFun 36 | | Move 37 | | Ret0 38 | | RetN 39 | | SelectTupleArity 40 | | SelectVal 41 | | SetNil 42 | | Test 43 | | TestHeap 44 | | Trim 45 | | TupleGetEl 46 | | TupleNew 47 | | TuplePut 48 | | TupleSetEl 49 | deriving (Eq, Ord) 50 | 51 | instance Show Opcode where 52 | show Alloc = "+alloc" 53 | show BsContextToBin = "+bs/ctx2bin" 54 | show BsInit = "+bs/init" 55 | show BsPutInteger = "+bs/puti" 56 | show BsRestore = "+bs/restore" 57 | show BsSave = "+bs/save" 58 | show CallBif = "+bif" 59 | show CallBifGc = "+bif/gc" 60 | show CallFun = "+callf" 61 | show CallGc = "+call/gc" 62 | show Call = "+call" 63 | show CallTail = "+call/tail" 64 | show CallTailDealloc = "+call/tail/dealloc" 65 | show Cons = "+cons" 66 | show Decons = "+decons" 67 | show Error = "+err" 68 | show Jump = "+jmp" 69 | show MakeFun = "+makefun" 70 | show Move = "+move" 71 | show Ret0 = "+ret0" 72 | show RetN = "+retn" 73 | show SelectTupleArity = "+selarity" 74 | show SelectVal = "+selval" 75 | show SetNil = "+nil" 76 | show Test = "+test" 77 | show TestHeap = "+testheap" 78 | show Trim = "+trim" 79 | show TupleGetEl = "+t_get" 80 | show TupleNew = "+t_new" 81 | show TuplePut = "+t_put" 82 | show TupleSetEl = "+t_set" 83 | 84 | bcOpEnumTable :: [(Opcode, Int)] 85 | bcOpEnumTable = 86 | [ (Error, 0) 87 | , (Test, 1) 88 | , (Alloc, 2) 89 | , (Move, 3) 90 | , (TestHeap, 4) 91 | , (Jump, 5) 92 | , (SetNil, 6) 93 | , (Trim, 7) 94 | , (MakeFun, 8) 95 | -- --------- 96 | , (Call, 10) 97 | , (CallGc, 11) 98 | , (CallTail, 12) 99 | , (CallTailDealloc, 13) 100 | , (CallFun, 14) 101 | , (CallBif, 15) 102 | , (CallBifGc, 16) 103 | , (Ret0, 17) 104 | , (RetN, 18) 105 | -- --------- 106 | , (TupleNew, 20) 107 | , (TuplePut, 21) 108 | , (TupleGetEl, 22) 109 | , (TupleSetEl, 23) 110 | -- --------- 111 | , (Decons, 30) 112 | , (Cons, 31) 113 | -- --------- 114 | , (SelectVal, 40) 115 | , (SelectTupleArity, 41) 116 | -- --------- 117 | , (BsInit, 50) 118 | , (BsContextToBin, 51) 119 | , (BsPutInteger, 52) 120 | , (BsSave, 53) 121 | , (BsRestore, 54) 122 | ] 123 | 124 | instance Enum Opcode where 125 | fromEnum = fromJust . flip L.lookup bcOpEnumTable 126 | toEnum = fromJust . flip lookup (map swap bcOpEnumTable) 127 | 128 | -- A combination of {Opcode and [Bit Encoded Args]} 129 | data Instruction = Instruction 130 | { iOp :: Opcode 131 | , iArgs :: B.BitsList 132 | } 133 | 134 | instance Show Instruction where 135 | show (Instruction op bits) = 136 | show (fromEnum op) ++ " " ++ show bits ++ comment 137 | where 138 | comment = ansiCyan ++ " ; " ++ show op ++ ansiReset 139 | 140 | makeInstruction :: Opcode -> B.BitsList -> Instruction 141 | makeInstruction op argBits = 142 | Instruction {iOp = op, iArgs = argBits} 143 | -- where 144 | -- op8 = fromIntegral $ fromEnum op 145 | 146 | harcodedFrequencies :: [Frequency Word8] 147 | harcodedFrequencies = L.map makeFreq bcOpEnumTable 148 | where 149 | makeFreq (_, opc) = H.Frequency (fromIntegral opc) (opc `quot` 4) 150 | -------------------------------------------------------------------------------- /Compiler/src/Bitcode/UerlFile.hs: -------------------------------------------------------------------------------- 1 | -- Module represents data structure and functions to create .uerl bitcode 2 | -- files with sections and stuff 3 | module Bitcode.UerlFile where 4 | 5 | import qualified Bits as B 6 | 7 | data UerlFile = UerlFile { 8 | header :: [B.Bits] 9 | } 10 | -------------------------------------------------------------------------------- /Compiler/src/Bits.hs: -------------------------------------------------------------------------------- 1 | -- Module provides facilities for compact encoding of tagged values in sub-byte 2 | -- units. Terms have a 3-bit tag. Integers have a 2-bit length tag choosing 3 | -- between 4-8-16 or 32 bit length. Reader/writer assume whether integer has 4 | -- a sign or not: this information is not stored. 5 | module Bits 6 | ( Bits 7 | , BitsList 8 | , bitsUB 9 | , bitsSB 10 | , signedFitsIn 11 | , makeBits 12 | ) where 13 | 14 | import qualified Data.Bits as DBits 15 | import qualified Data.List as L 16 | import qualified Data.ByteString as DBS 17 | 18 | -- An unsigned Word value which fits into Word8 bits 19 | data Bits 20 | = BitsSignedBig Int 21 | Int 22 | | BitsUnsignedBig Int 23 | Int 24 | | Bytes DBS.ByteString 25 | 26 | instance Show Bits where 27 | show (BitsSignedBig v bits) = show v ++ ":" ++ show bits ++ "/signed" 28 | show (BitsUnsignedBig v bits) = show v ++ ":" ++ show bits 29 | show (Bytes bs) = "\"" ++ show bs ++ "\"" 30 | 31 | -- A sequence of non-byte-aligned bits which will be joined together. 32 | -- Assuming the reader is aware what bits mean and can decode this sequence. 33 | type BitsList = [Bits] 34 | 35 | signedFitsIn :: (DBits.Bits a, Num a, Ord a) => a -> Int -> Bool 36 | signedFitsIn svalue bits = 37 | let limit = 1 `DBits.shiftL` (bits - 1) 38 | in svalue >= -limit && svalue <= limit - 1 39 | 40 | bitsUB :: Int -> Int -> Bits 41 | bitsUB val bits 42 | | val >= 0 && val < (1 `DBits.shiftL` bits) = BitsUnsignedBig val bits 43 | 44 | bitsSB :: Int -> Int -> Bits 45 | bitsSB val bits 46 | | signedFitsIn val bits = BitsSignedBig val bits 47 | 48 | -- Creates an unsigned integer from bit boolean sequence (no size preserved, 49 | -- use original input length to know the bit size) 50 | bitPack :: [Bool] -> Int 51 | bitPack inp = outp 52 | where 53 | foldFn (val, pos) True = (val `DBits.setBit` pos, pos + 1) 54 | foldFn (val, pos) False = (val, pos + 1) 55 | (outp, _) = L.foldl foldFn (0, 0) inp 56 | 57 | makeBits :: [Bool] -> Bits 58 | makeBits val = bitsUB (bitPack val) (length val) 59 | -------------------------------------------------------------------------------- /Compiler/src/Pass/PassAsm.hs: -------------------------------------------------------------------------------- 1 | module Pass.PassAsm 2 | ( transform 3 | ) where 4 | 5 | import qualified Asm as A 6 | import qualified Asm.Func as AF 7 | import qualified Asm.Mod as AM 8 | import qualified Bitcode as B 9 | import qualified Bitcode.Func as BF 10 | import qualified Bitcode.Mod as BM 11 | import qualified Bitcode.Op as BO 12 | import qualified Term as T 13 | import Uerlc 14 | 15 | import qualified Control.Monad.Except as MEx 16 | import qualified Control.Monad.State as S 17 | import qualified Data.Map as Map 18 | 19 | -- Given Asm module produce Bitcode module or throw an error 20 | transform :: AM.Module -> BM.Module 21 | transform amod = S.execState (transformM funs) BM.new 22 | where 23 | funs = Map.elems $ AM.funs amod 24 | 25 | -- given list of Asm funs and a Bitcode module, update module with 26 | -- funs that are transformed to Bitcode funs 27 | transformM :: [AF.Func] -> BM.ModuleState (CompileErrorOr ()) 28 | transformM [] = return $ Right () 29 | transformM (fun:fTail) = do 30 | let nameArity = AF.name fun 31 | Right bcFun <- transformFnM fun 32 | updateFunM nameArity bcFun 33 | transformM fTail 34 | 35 | -- Updates/writes a func in a bitcode module, returns an updated module 36 | updateFunM :: T.FunArity -> BF.Func -> BM.ModuleState () 37 | updateFunM nameArity f = do 38 | bcMod0 <- S.get 39 | let funs0 = BM.funs bcMod0 40 | funs1 = Map.insert nameArity f funs0 41 | S.put $ bcMod0 {BM.funs = funs1} 42 | return () 43 | 44 | -- Given an Asm func converts it to a Bitcode func, also updates 45 | -- the module with whatever is found on the way (atoms, literals etc) 46 | transformFnM :: AF.Func -> BM.ModuleState (CompileErrorOr BF.Func) 47 | transformFnM fn = do 48 | let asmCode = AF.code fn 49 | -- bitcode <- foldM foldOpHelper [] asmCode 50 | trResult <- transformAsmOpsM [] asmCode 51 | case trResult of 52 | Right bitcode -> 53 | let outFn = BF.Func {BF.bcfName = AF.name fn, BF.bcfCode = bitcode} 54 | in return (Right outFn) 55 | Left e -> return $ Left e 56 | 57 | -- Given an accumulator (bitcode ops) and input (a list of asm 58 | -- opcodes) returns a list of bytecodes 59 | transformAsmOpsM :: 60 | [BO.Instruction] 61 | -> [A.Instruction] 62 | -> BM.ModuleState (CompileErrorOr [BO.Instruction]) 63 | transformAsmOpsM acc [] = return $ Right (reverse acc) 64 | transformAsmOpsM acc (aop:remainingAops) = do 65 | trResult <- transform1M aop 66 | case trResult of 67 | Right bcop -> transformAsmOpsM (bcop ++ acc) remainingAops 68 | Left e -> return $ Left e 69 | 70 | -- For those cases when 1:1 simple mapping between asm and bitcode 71 | -- is enough. For complex cases add a clause in transformAsmOpsM 72 | transform1M :: A.Instruction -> BM.ModuleState (CompileErrorOr [BO.Instruction]) 73 | transform1M (A.AComment _s) = return $ Right [] 74 | transform1M (A.ALabel _lb) = return $ Right [] 75 | transform1M (A.ALine _ln) = return $ Right [] 76 | transform1M (A.AError e) = B.invokeErrorM e 77 | transform1M (A.ATest tname onfail args maybeLive dst) = 78 | B.testM tname onfail args maybeLive dst 79 | transform1M (A.AAlloc need live) = B.allocM need live 80 | transform1M (A.AMove src dst) = B.moveM src dst 81 | transform1M (A.ACall arity codeLoc callType) = B.callM arity codeLoc callType 82 | transform1M (A.ATestHeap needH live) = B.testHeapM needH live 83 | transform1M (A.ATupleNew sz dst) = B.tupleNewM sz dst 84 | transform1M (A.ATuplePut val) = B.tuplePutM val 85 | transform1M (A.ATupleGetEl src i dst) = B.tupleGetElM src i dst 86 | transform1M (A.ATupleSetEl val index dst) = B.tupleSetElM val index dst 87 | transform1M (A.ARet dealloc) = B.retM dealloc 88 | transform1M (A.ACallBif name onfail args callType dst) = 89 | B.callBifM name onfail args callType dst 90 | transform1M (A.ADecons src dstH dstT) = B.deconsM src dstH dstT 91 | transform1M (A.ACons h t dst) = B.consM h t dst 92 | transform1M (A.ASelect selType val onfail jtab) = 93 | B.selectM selType val onfail jtab 94 | transform1M (A.AJump lbl) = B.jumpM lbl 95 | transform1M (A.ACallFun arity) = B.callFunM arity 96 | transform1M (A.ASetNil dst) = B.setNilM dst 97 | transform1M (A.ATrim n) = B.trimM n 98 | transform1M (A.AMakeFun lbl nfree) = B.makeFunM lbl nfree 99 | transform1M (A.ABsContextToBin src) = B.bsContextToBinM src 100 | transform1M (A.ABsSave src index) = B.bsSaveM src index 101 | transform1M (A.ABsRestore src index) = B.bsRestoreM src index 102 | transform1M (A.ABsInit sz gcLive dst onFail) = B.bsInitM sz gcLive dst onFail 103 | transform1M (A.ABsPutInteger src bFlags dst) = B.bsPutIntegerM src bFlags dst 104 | transform1M op = return $ Uerlc.errM $ "Don't know how to compile: " ++ show op 105 | -- return $ Right [] 106 | -------------------------------------------------------------------------------- /Compiler/src/Pass/PassBitcode.hs: -------------------------------------------------------------------------------- 1 | -- Second pass for Asm, processes bitcode instructions and creates bit stream 2 | -- output with compressed opcodes 3 | module Pass.PassBitcode 4 | ( transform 5 | ) where 6 | 7 | import qualified Asm.Mod as AM 8 | import qualified Bitcode.Mod as BM 9 | import qualified Bitcode.UerlFile as BF 10 | 11 | import qualified Control.Monad.State as S 12 | import qualified Data.Map as Map 13 | 14 | -- Given Bitcode module produce Bitcode compressed opcodes 15 | transform :: BM.Module -> BF.UerlFile 16 | transform bcmod = 17 | let funs = BM.funs bcmod 18 | funs1 = S.execState (transformM funs) [] 19 | -------------------------------------------------------------------------------- /Compiler/src/Term.hs: -------------------------------------------------------------------------------- 1 | module Term 2 | ( Term(..) 3 | , MFArity(..) 4 | , FunArity(..) 5 | , strFromErl 6 | , bigintFromErl 7 | , intFromErl 8 | , funarityFromErl 9 | ) where 10 | 11 | import Data.List 12 | 13 | data Term 14 | = Atom String 15 | | BinaryStr String 16 | | ErlComment String 17 | | ErlInt Integer 18 | | ErlList [Term] 19 | | ErlStr String 20 | | ErlTuple [Term] 21 | deriving (Eq, Ord) 22 | 23 | instance Show Term where 24 | show (Atom s) = "'" ++ s ++ "'" 25 | show (ErlList items) = 26 | let str_items = map show items 27 | in "[" ++ intercalate "," str_items ++ "]" 28 | show (ErlTuple items) = 29 | let str_items = map show items 30 | in "{" ++ intercalate "," str_items ++ "}" 31 | show (ErlComment c) = "(% " ++ c ++ " %)" 32 | show (ErlInt i) = show i 33 | show (ErlStr s) = show s 34 | show (BinaryStr s) = "<<\"" ++ show s ++ "\">>" 35 | 36 | data FunArity = 37 | FunArity String 38 | Int 39 | deriving (Eq, Ord) 40 | 41 | instance Show FunArity where 42 | show (FunArity f a) = f ++ "/" ++ show a 43 | 44 | data MFArity = 45 | MFArity String 46 | String 47 | Int 48 | deriving (Eq, Ord) 49 | 50 | instance Show MFArity where 51 | show (MFArity m f a) = m ++ ":" ++ f ++ "/" ++ show a 52 | 53 | -- unwrap a string from an Term Atom or ErlStr 54 | strFromErl :: Term -> Maybe String 55 | strFromErl (Atom s) = Just s 56 | strFromErl (ErlStr s) = Just s 57 | strFromErl _sxpr = Nothing 58 | 59 | -- Unwrap a long integer from an SExpression 60 | bigintFromErl :: Term -> Maybe Integer 61 | bigintFromErl (ErlInt i) = Just i 62 | bigintFromErl _sxpr = Nothing 63 | 64 | -- Unwrap a short machine integer from an SExpression 65 | intFromErl :: Term -> Maybe Int 66 | intFromErl (ErlInt i) = Just (fromIntegral i) 67 | intFromErl _sxpr = Nothing 68 | 69 | -- Given two Term values from BEAM S parse produce a Funarity pair 70 | funarityFromErl :: Term -> Term -> FunArity 71 | funarityFromErl fname farity = FunArity fname' farity' 72 | where 73 | Just fname' = strFromErl fname 74 | Just farity' = intFromErl farity 75 | -------------------------------------------------------------------------------- /Compiler/src/Uerlc.hs: -------------------------------------------------------------------------------- 1 | module Uerlc 2 | ( err 3 | , errM 4 | , CompileErrorOr 5 | , CompileException(..) 6 | , ansiCyan 7 | , ansiRed 8 | , ansiReset 9 | ) where 10 | 11 | import Control.Exception 12 | import Data.Typeable 13 | import System.Console.ANSI 14 | 15 | newtype CompileException = 16 | CompileException String 17 | deriving (Typeable) 18 | 19 | instance Show CompileException where 20 | show (CompileException s) = ansiRed ++ s ++ ansiReset 21 | 22 | instance Exception CompileException 23 | 24 | -- To combine with success value and create a monadic error type 25 | type CompileErrorOr = Either CompileException 26 | 27 | err :: String -> a 28 | err s = throw $ CompileException s 29 | 30 | -- Left value for use with monadic CompileErrorOr x 31 | errM :: String -> Either CompileException b 32 | errM s = Left $ CompileException s 33 | 34 | -- Helper to paint the text red 35 | ansiRed :: String 36 | ansiRed = setSGRCode [SetColor Foreground Vivid Red] 37 | 38 | ansiCyan :: String 39 | ansiCyan = setSGRCode [SetColor Foreground Dull Cyan] 40 | 41 | -- Helper to uncolor the text 42 | ansiReset :: String 43 | ansiReset = setSGRCode [Reset] 44 | -------------------------------------------------------------------------------- /Compiler/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # http://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # resolver: ghcjs-0.1.0_ghc-7.10.2 15 | # resolver: 16 | # name: custom-snapshot 17 | # location: "./custom-snapshot.yaml" 18 | resolver: lts-8.22 19 | 20 | # User packages to be built. 21 | # Various formats can be used as shown in the example below. 22 | # 23 | # packages: 24 | # - some-directory 25 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 26 | # - location: 27 | # git: https://github.com/commercialhaskell/stack.git 28 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 29 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a 30 | # extra-dep: true 31 | # subdirs: 32 | # - auto-update 33 | # - wai 34 | # 35 | # A package marked 'extra-dep: true' will only be built if demanded by a 36 | # non-dependency (i.e. a user package), and its test suites and benchmarks 37 | # will not be run. This is useful for tweaking upstream packages. 38 | packages: 39 | - '.' 40 | 41 | #- location: 42 | # git: https://github.com/wdanilo/haskell-logger.git 43 | # commit: aba5ef8f07d172466dd45e0dd078b25ec5353cda 44 | 45 | # Dependency packages to be pulled from upstream that are not in the resolver 46 | # (e.g., acme-missiles-0.3) 47 | extra-deps: [ 48 | binary-strict-0.4.8.3 49 | ] 50 | 51 | # Override default flag values for local packages and extra-deps 52 | flags: {} 53 | 54 | # Extra package databases containing global packages 55 | extra-package-dbs: [] 56 | 57 | # Control whether we use the GHC we find on the path 58 | # system-ghc: true 59 | # 60 | # Require a specific version of stack, using version ranges 61 | # require-stack-version: -any # Default 62 | # require-stack-version: ">=1.1" 63 | # 64 | # Override the architecture used by stack, especially useful on Windows 65 | # arch: i386 66 | # arch: x86_64 67 | # 68 | # Extra directories used by stack for building 69 | # extra-include-dirs: [/path/to/dir] 70 | # extra-lib-dirs: [/path/to/dir] 71 | # 72 | # Allow a newer minor version of GHC than the snapshot specifies 73 | # compiler-check: newer-minor -------------------------------------------------------------------------------- /Compiler/test/Spec.hs: -------------------------------------------------------------------------------- 1 | main :: IO () 2 | main = putStrLn "Test suite not yet implemented" 3 | -------------------------------------------------------------------------------- /Compiler/testData/.gitignore: -------------------------------------------------------------------------------- 1 | *.kernel 2 | *.S 3 | -------------------------------------------------------------------------------- /Compiler/testData/test1.erl: -------------------------------------------------------------------------------- 1 | -module(test1). 2 | -compile([export_all]). 3 | 4 | test1() -> 5 | map_1(0, {1, 2, 3, test_return_var(4)}). 6 | 7 | map_1(_, undefined) -> undefined; 8 | map_1(F, {K, V, Smaller, Larger}) -> 9 | {K, F(K, V), map_1(F, Smaller), map_1(F, Larger)}. 10 | 11 | test_return_var(X) -> 12 | Y = X * 2, 13 | Y. 14 | -------------------------------------------------------------------------------- /Compiler/uerlc.cabal: -------------------------------------------------------------------------------- 1 | name: uerlc 2 | version: 0.1.0.0 3 | -- synopsis: 4 | -- description: 5 | homepage: https://uerlang.org 6 | license: BSD3 7 | license-file: LICENSE 8 | author: Dmytro Lytovchenko 9 | maintainer: dmytro.lytovchenko@gmail.com 10 | copyright: 2017 Dmytro Lytovchenko 11 | category: VM 12 | build-type: Simple 13 | extra-source-files: README.md 14 | cabal-version: >=1.10 15 | 16 | library 17 | hs-source-dirs: src 18 | exposed-modules: Asm 19 | , Asm.Binary 20 | , Asm.Func 21 | , Asm.Instruction 22 | , Asm.Locations 23 | , Asm.Mod 24 | , BeamSParser 25 | , Bits 26 | , Bitcode 27 | , Bitcode.Encode 28 | , Bitcode.Encode.Const 29 | , Bitcode.Encode.Huffman 30 | , Bitcode.Func 31 | , Bitcode.Mod 32 | , Bitcode.Op 33 | , Term 34 | , Pass.PassAsm 35 | , Pass.PassBeamS 36 | , Pass.PassBitcode 37 | , Uerlc 38 | build-depends: base >= 4.7 && < 5 39 | , ansi-terminal 40 | , binary 41 | , binary-strict 42 | , bytestring 43 | , containers 44 | , exceptions 45 | , mtl 46 | , parsec 47 | default-language: Haskell2010 48 | 49 | executable uerlc-exe 50 | hs-source-dirs: app 51 | main-is: Main.hs 52 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 53 | build-depends: base 54 | , ansi-terminal 55 | , bytestring 56 | , exceptions 57 | , hslogger 58 | , uerlc 59 | default-language: Haskell2010 60 | 61 | test-suite uerlc-test 62 | type: exitcode-stdio-1.0 63 | hs-source-dirs: test 64 | main-is: Spec.hs 65 | build-depends: base 66 | , containers 67 | , uerlc 68 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 69 | default-language: Haskell2010 70 | 71 | source-repository head 72 | type: git 73 | location: https://github.com/kvakvs/E4VM 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Dmytro Lytovchenko . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | * The names of its contributors may not be used to endorse or promote 16 | products derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # For building the Compiler and the Runtime please look at Makefiles in 2 | # their directories 3 | 4 | .PHONY: docs 5 | docs: 6 | cd docs-src && \ 7 | $(MAKE) html && \ 8 | rm -rf ../docs/* && \ 9 | mv -f _build/html/* _build/html/.n* ../docs/ && \ 10 | echo "uerlang.org" > ../docs/CNAME 11 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Note 2 | ==== 3 | 4 | This project is not moving forward (as you probably noticed) and ends are 5 | not yet properly connected. I.e. the precompiler generates compressed bitcode 6 | but the runtime does not handle it yet. 7 | 8 | The general idea was to create a portable bit-compressed bytecode format and a 9 | tiny runtime for embedded systems. 10 | 11 | Here's my talk on EUC 2017 (Stockholm) explaining the history of this project 12 | and why it is here https://www.youtube.com/watch?v=6NUPormxgw8 13 | 14 | MicroErlang 15 | =========== 16 | 17 | For documentation and getting started please refer to http://uerlang.org 18 | 19 | Building Documentation 20 | ---------------------- 21 | 22 | Having Sphinx installed run ``make docs`` in the root directory of the project. 23 | It will populate ``docs/`` with the new copy of the documentation and website 24 | content. 25 | -------------------------------------------------------------------------------- /Runtime/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Chromium 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Inline 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: false 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: true 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | BeforeCatch: false 32 | BeforeElse: false 33 | IndentBraces: false 34 | BreakBeforeBinaryOperators: None 35 | BreakBeforeBraces: Attach 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializersBeforeComma: false 38 | ColumnLimit: 80 39 | CommentPragmas: '^ IWYU pragma:' 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 41 | ConstructorInitializerIndentWidth: 2 42 | ContinuationIndentWidth: 2 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | DisableFormat: false 46 | ExperimentalAutoDetectBinPacking: false 47 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 48 | IncludeCategories: 49 | - Regex: '^<.*\.h>' 50 | Priority: 1 51 | - Regex: '^<.*' 52 | Priority: 2 53 | - Regex: '.*' 54 | Priority: 3 55 | IndentCaseLabels: true 56 | IndentWidth: 2 57 | IndentWrappedFunctionNames: false 58 | KeepEmptyLinesAtTheStartOfBlocks: false 59 | MacroBlockBegin: '' 60 | MacroBlockEnd: '' 61 | MaxEmptyLinesToKeep: 1 62 | NamespaceIndentation: None 63 | ObjCBlockIndentWidth: 2 64 | ObjCSpaceAfterProperty: false 65 | ObjCSpaceBeforeProtocolList: false 66 | PenaltyBreakBeforeFirstCallParameter: 1 67 | PenaltyBreakComment: 300 68 | PenaltyBreakFirstLessLess: 120 69 | PenaltyBreakString: 1000 70 | PenaltyExcessCharacter: 1000000 71 | PenaltyReturnTypeOnItsOwnLine: 200 72 | PointerAlignment: Left 73 | SpaceAfterCStyleCast: false 74 | SpaceBeforeAssignmentOperators: true 75 | SpaceBeforeParens: ControlStatements 76 | SpaceInEmptyParentheses: false 77 | SpacesBeforeTrailingComments: 2 78 | SpacesInAngles: false 79 | SpacesInContainerLiterals: true 80 | SpacesInCStyleCastParentheses: false 81 | SpacesInParentheses: false 82 | SpacesInSquareBrackets: false 83 | Standard: Auto 84 | TabWidth: 8 85 | UseTab: Never 86 | ... 87 | 88 | -------------------------------------------------------------------------------- /Runtime/.gitignore: -------------------------------------------------------------------------------- 1 | size_report*.txt 2 | cmake-build-*/ 3 | build-*/ 4 | -------------------------------------------------------------------------------- /Runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.5) 2 | PROJECT(E4VM CXX) 3 | 4 | SET(CMAKE_CXX_STANDARD 14) 5 | 6 | SET(E4_INVOKE_PVS 0) 7 | 8 | IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 9 | SET(E4_GCC 0) 10 | ELSE() 11 | SET(E4_GCC 1) 12 | ENDIF() 13 | 14 | SET(COMPILATION_INTERMEDIATE_RESULTS 0) 15 | 16 | ## We do not use standard C++ library here, but we DO use C++ startup/exit code 17 | #add_definitions(-nostdinc++) 18 | 19 | INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) 20 | 21 | ## Erlang Runtime system for E4VM: types, bifs, memory etc 22 | ## Minus the VM itself this later can become a runtime library for compiled Erl 23 | SET(E4_RUNTIME_SOURCES 24 | include/e4.h 25 | include/e4rt/atom_store.h 26 | include/e4rt/binary.h 27 | include/e4rt/box.h 28 | include/e4rt/bytecode.h 29 | include/e4rt/code_mgr.h 30 | include/e4rt/dist.h 31 | include/e4rt/ext_term_format.h 32 | include/e4rt/heap.h 33 | include/e4rt/messages.h 34 | include/e4rt/module.h 35 | include/e4rt/process.h 36 | include/e4rt/range_checker.h 37 | include/e4rt/scheduler.h 38 | include/e4rt/term.h 39 | include/e4rt/term_as_map_key.h 40 | include/e4rt/term_tag.h 41 | include/e4rt/vm.h 42 | src/e4rt/atom_store.cpp 43 | src/e4rt/bytecode.cpp 44 | src/e4rt/code_mgr.cpp 45 | src/e4rt/ext_term_format.cpp 46 | src/e4rt/module.cpp 47 | src/e4rt/process.cpp 48 | src/e4rt/scheduler.cpp 49 | src/e4rt/term.cpp 50 | src/e4rt/vm.cpp 51 | src/e4rt/vm_loop.cpp 52 | ) 53 | 54 | ## Helpers and OS abstraction library 55 | SET(E4_PLATF_SOURCES 56 | include/e4platf/byte_stream_reader.h 57 | include/e4platf/conf.h 58 | include/e4platf/debug.h 59 | include/e4platf/fs.h 60 | include/e4platf/mem.h 61 | include/e4platf/types.h 62 | src/e4platf/byte_stream_reader.cpp 63 | src/e4platf/debug.cpp 64 | src/e4platf/fs.cpp 65 | src/e4platf/mem.cpp 66 | ) 67 | 68 | # Minimal badly written replacements for C++ stuff optimized for size 69 | SET(E4_STD_SOURCES 70 | include/e4std/array.h 71 | include/e4std/complicated.h 72 | include/e4std/free_fun.h 73 | include/e4std/map.h 74 | include/e4std/ptr.h 75 | include/e4std/sizes.h 76 | include/e4std/string.h 77 | include/e4std/stuff.h 78 | include/e4std/vector.h 79 | include/e4std/vector_impl.h 80 | include/e4std/view.h 81 | src/e4std/free_fun.cpp 82 | src/e4std/stuff.cpp 83 | src/e4std/vector.cpp 84 | ) 85 | 86 | SET(EMU_SOURCES src/main.cpp) 87 | 88 | IF (CMAKE_BUILD_TYPE MATCHES Debug) 89 | ADD_DEFINITIONS( 90 | -O0 91 | -ggdb 92 | -DE4DEBUG=1 93 | -fno-unroll-loops 94 | ) 95 | ELSE () 96 | ADD_DEFINITIONS( 97 | -Os 98 | -g0 99 | -DE4DEBUG=0 100 | ) 101 | ENDIF () 102 | 103 | ADD_DEFINITIONS( 104 | -DE4FEATURE_FS=1 105 | -DE4_WORD_SIZE=64 106 | ) 107 | ADD_DEFINITIONS( 108 | -std=c++17 109 | -fno-rtti 110 | #-fno-exceptions 111 | ) 112 | ADD_DEFINITIONS( 113 | -Wall -Werror 114 | -Wno-unused-private-field 115 | ) 116 | 117 | IF(CMAKE_CXX_COMPILER MATCHES clang) 118 | ADD_DEFINITIONS( 119 | -Weverything 120 | -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-gnu-anonymous-struct 121 | -Wno-zero-length-array -Wno-nested-anon-types 122 | -Wno-format-nonliteral -Wno-global-constructors 123 | ) 124 | ELSE() 125 | IF(E4_GCC) 126 | ADD_DEFINITIONS( 127 | -ffunction-sections 128 | -Wl,--gc-sections 129 | ) 130 | ENDIF() 131 | ENDIF() 132 | 133 | ADD_LIBRARY(e4_runtime ${E4_RUNTIME_SOURCES}) 134 | ADD_LIBRARY(e4_platform ${E4_PLATF_SOURCES}) 135 | ADD_LIBRARY(e4_std ${E4_STD_SOURCES}) 136 | 137 | ADD_EXECUTABLE(e4emu ${EMU_SOURCES}) 138 | TARGET_LINK_LIBRARIES(e4emu 139 | e4_runtime e4_platform e4_std) 140 | 141 | IF(COMPILATION_INTERMEDIATE_RESULTS) 142 | TARGET_COMPILE_OPTIONS(e4_std PUBLIC -save-temps) 143 | TARGET_COMPILE_OPTIONS(e4_platform PUBLIC -save-temps) 144 | TARGET_COMPILE_OPTIONS(e4_runtime PUBLIC -save-temps) 145 | 146 | TARGET_COMPILE_OPTIONS(e4emu-debug PUBLIC -save-temps) 147 | ENDIF() 148 | 149 | #IF(E4_GCC) 150 | # IF(CMAKE_BUILD_TYPE MATCHES Debug) 151 | # SET_TARGET_PROPERTIES(e4emu PROPERTIES LINK_FLAGS 152 | # "-flto -static -static-libgcc -static-libstdc++") 153 | # ELSE() 154 | # SET_TARGET_PROPERTIES(e4emu PROPERTIES LINK_FLAGS 155 | # "-flto -static -static-libgcc -static-libstdc++") 156 | # ENDIF() 157 | #ELSE() 158 | #ENDIF(E4_GCC) 159 | 160 | #SET_TARGET_PROPERTIES(e4emu PROPERTIES LINK_FLAGS 161 | # "-static -static-libgcc -static-libstdc++") 162 | 163 | ## 164 | ## PVS-Studio Static Checker 165 | ## 166 | IF(E4_INVOKE_PVS) 167 | INCLUDE(PVS-Studio.cmake) 168 | pvs_studio_add_target( 169 | TARGET analyze ALL 170 | OUTPUT FORMAT tasklist 171 | PREPROCESSOR gcc 172 | LOG "PVS-report.tasks" 173 | ANALYZE e4_runtime e4_platform e4_std e4emu 174 | CXX_FLAGS ${PREPROCESSOR_ADDITIONAL_FLAGS} 175 | C_FLAGS ${PREPROCESSOR_ADDITIONAL_FLAGS} 176 | CONFIG "${CMAKE_SOURCE_DIR}/PVS-Studio.cfg" 177 | ) 178 | ENDIF() -------------------------------------------------------------------------------- /Runtime/Makefile: -------------------------------------------------------------------------------- 1 | E4BUILDDIR=cmake-build-debug 2 | E4EMU=${E4BUILDDIR}/e4emu 3 | 4 | ${E4EMU}: compile 5 | 6 | .PHONY: gdb 7 | gdb: compile 8 | gdb ${E4EMU} 9 | 10 | .PHONY: compile 11 | compile: 12 | cd ${E4BUILDDIR} && $(MAKE) -j; cd .. 13 | 14 | .PHONY: lic-add 15 | lic-add: 16 | rm -rf TMP && \ 17 | copyright-header --license-file license.snippet \ 18 | --add-path src:include \ 19 | --output-dir TMP 20 | .PHONY: lic-rm 21 | lic-rm: 22 | rm -rf TMP && \ 23 | copyright-header --license-file license.snippet \ 24 | --remove-path src:include \ 25 | --output-dir TMP 26 | 27 | # Convert and print PVS-studio results if it wasn't done automatically 28 | .PHONY: plog 29 | plog: 30 | plog-converter -t tasklist -a "GA:1,2;64:1;CS" \ 31 | ${E4BUILDDIR}/PVS-Studio/src/main.cpp.plog 32 | 33 | .PHONY: valgrind 34 | valgrind: compile 35 | valgrind --log-file=valgrind.log \ 36 | ${E4EMU} 37 | 38 | # 39 | # Invoke nm and size tools to print section sizes of the executable 40 | # 41 | .PHONY: sizes 42 | sizes: ${E4EMU} 43 | nm --print-size --size-sort --radix=d ${E4EMU} | sort -k 2 -r > size_report_1.txt && \ 44 | size -A -d ${E4EMU} > size_report_2.txt 45 | -------------------------------------------------------------------------------- /Runtime/PVS-Studio.cfg: -------------------------------------------------------------------------------- 1 | analysis-mode=4 2 | -------------------------------------------------------------------------------- /Runtime/doc/3eam-format.rst: -------------------------------------------------------------------------------- 1 | File Format for 3EAM files 2 | ========================== 3 | 4 | This format is somewhat similar but also different from the original 5 | Ericsson's Erlang/OTP BEAM format. 6 | Emphasis is to preprocess and simplify the loading as much as possible. 7 | 8 | 3EAM are rewritten BEAM files with some processing done on them. 9 | They have reduced instruction set. 10 | Encoding of data is simplified to varint (UTF-8 algorithm where eldest bit). 11 | 12 | File header 13 | ----------- 14 | 15 | File begins with initial marker "3EAM". 16 | It is followed by section markers in no particular order. They can be: 17 | 18 | * "Expt", exports table; 19 | * "Atom", atom table; 20 | * "Code", code section. 21 | 22 | Section marker is followed by varint byte size. 23 | 24 | Exports section 25 | --------------- 26 | 27 | Section contains a list of pairs [{varint function_name, varint arity}]. 28 | Function name is encoded as an index in the atom table. 29 | 30 | Atom section 31 | ------------ 32 | 33 | Atom table contains all atoms collected from the module in form of strings. 34 | 35 | After the section header "Atom" and varint section_size goes varint atom_count. 36 | Each string is encoded as [varint size, utf8 characters] for each atom, 37 | beginning with the index 0. 38 | Atom at position 0 is always the module name. 39 | 40 | Code section 41 | ------------ 42 | 43 | Code is encoded directly from BEAM assembly output by the Erlang compiler 44 | with some simplifications compared to original BEAM files. 45 | Some data is omitted. 46 | Numbers are simplified. 47 | This is done to reduce the loader and decoder size. 48 | 49 | Code contains list of functions. Each function begins with ?MARK_FUNCTION=255, 50 | followed by varint atom_name, varint arity, varint operator_count and 51 | then list of operators. 52 | 53 | Code end is marked by ?MARK_CODE_END=254. 54 | 55 | See gcompile module for opcodes. Arguments are encoded as tagged values, followed 56 | by the value itself: 57 | 58 | * ?VAL_X=1 denotes an X register, followed by varint x_register_index 59 | * ?VAL_Y=2 denotes an Y stack slot, followed by varint y_index 60 | * ?VAL_ATOM=3 followed by varint atom index 61 | * more codes ... (consult gcompiler.erl) -------------------------------------------------------------------------------- /Runtime/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BASE=`pwd` 4 | #for dir in src src/bif src/miniz src/platf include include/bif include/struct test; do 5 | for F in `find . -name \*.h -print -o -name \*.cpp -print -name \*.c -print`; do 6 | echo "===> $F" 7 | clang-format-3.8 -i $F 8 | done -------------------------------------------------------------------------------- /Runtime/include/e4.h: -------------------------------------------------------------------------------- 1 | /* * This is an open source non-commercial project. Dear PVS-Studio, please 2 | * check it. 3 | * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "e4platf/types.h" 9 | 10 | #include "e4rt/term.h" 11 | #include "e4rt/vm.h" 12 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/conf.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | namespace e4 { 9 | 10 | // 11 | //====== BEGIN FEATURES ====== 12 | // 13 | 14 | #ifndef E4FEATURE_COLOR_CONSOLE 15 | #define E4FEATURE_COLOR_CONSOLE 0 16 | #endif 17 | 18 | #ifndef E4FEATURE_FS 19 | #define E4FEATURE_FS 0 20 | #endif 21 | 22 | #ifndef E4FEATURE_ERLDIST 23 | #define E4FEATURE_ERLDIST 0 24 | #endif 25 | 26 | #ifndef E4FEATURE_FLOAT 27 | #define E4FEATURE_FLOAT 0 28 | #endif 29 | 30 | #ifndef E4FEATURE_BIGNUM 31 | #define E4FEATURE_BIGNUM 0 32 | #endif 33 | 34 | #ifndef E4FEATURE_MAPS 35 | #define E4FEATURE_MAPS 0 36 | #endif 37 | 38 | #ifndef E4FEATURE_HOTCODELOAD 39 | #define E4FEATURE_HOTCODELOAD 0 40 | #endif 41 | 42 | namespace feat { 43 | constexpr bool color_console() { return (E4FEATURE_COLOR_CONSOLE != 0); } 44 | constexpr bool fs() { return (E4FEATURE_FS != 0); } 45 | constexpr bool distribution() { return (E4FEATURE_ERLDIST != 0); } 46 | constexpr bool floating_point() { return (E4FEATURE_FLOAT != 0); } 47 | constexpr bool bignum() { return (E4FEATURE_BIGNUM != 0); } 48 | constexpr bool maps() { return (E4FEATURE_MAPS != 0); } 49 | constexpr bool hot_code_load() { return (E4FEATURE_HOTCODELOAD != 0); } 50 | } // ns feat 51 | 52 | // 53 | //====== END FEATURES ====== 54 | // 55 | 56 | constexpr bool DEBUG_MODE = (E4DEBUG != 0); 57 | 58 | 59 | #if defined(__arm__) 60 | #define E4_ARM 1 61 | #else 62 | #define E4_ARM 0 63 | #endif 64 | 65 | 66 | #undef BIG_ENDIAN 67 | #undef LITTLE_ENDIAN 68 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 69 | #define E4_BIG_ENDIAN 0 70 | constexpr bool BIG_ENDIAN = false; 71 | #else 72 | #define E4_BIG_ENDIAN 1 73 | constexpr bool BIG_ENDIAN = true; 74 | #endif 75 | 76 | #define DECL_EXCEPTION(NAME) \ 77 | class NAME##Error : public e4::RuntimeError { \ 78 | public: \ 79 | NAME##Error(const char* e) : e4::RuntimeError(e) {} \ 80 | virtual const char* what() const noexcept; \ 81 | }; 82 | #define IMPL_EXCEPTION(NAME) \ 83 | const char* NAME##Error::what() const noexcept { \ 84 | return e4::RuntimeError::what(); \ 85 | } 86 | #define DECL_IMPL_EXCEPTION(NAME) DECL_EXCEPTION(NAME) IMPL_EXCEPTION(NAME) 87 | 88 | #define E4_NORETURN __attribute__((noreturn)) 89 | 90 | #if __cplusplus > 201402L 91 | #if __has_cpp_attribute(nodiscard) 92 | #define E4_NODISCARD [[nodiscard]] 93 | #elif __has_cpp_attribute(gnu::warn_unused_result) 94 | #define E4_NODISCARD [[gnu::warn_unused_result]] 95 | #endif 96 | #else 97 | #define E4_NODISCARD 98 | #endif 99 | 100 | #if __cplusplus > 201402L 101 | #if __has_cpp_attribute(maybe_unused) 102 | #define E4_MAYBE_UNUSED [[maybe_unused]] 103 | #elif __has_cpp_attribute(gnu::unused) 104 | #define E4_MAYBE_UNUSED [[gnu::unused]] 105 | #endif 106 | #else 107 | #define E4_MAYBE_UNUSED 108 | #endif 109 | 110 | #if __has_cpp_attribute(fallthrough) 111 | #define E4_FALLTHROUGH [[fallthrough]] 112 | #elif __has_cpp_attribute(clang::fallthrough) 113 | #define E4_FALLTHROUGH [[clang::fallthrough]] 114 | #else 115 | #define E4_FALLTHROUGH 116 | #endif 117 | 118 | } // ns e4 119 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/debug.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4platf/conf.h" 9 | 10 | // A decorator to explicitly mark a mutable argument 11 | #define MUTABLE 12 | 13 | namespace e4 { 14 | 15 | E4_NORETURN void fail(const char* m); 16 | E4_NORETURN void failf(const char* format, ...); 17 | 18 | #if E4DEBUG 19 | void debug_printf(const char* format, ...); 20 | #endif // E4DEBUG 21 | 22 | // the following are UBUNTU/LINUX ONLY terminal color codes. 23 | #if E4FEATURE_COLOR_CONSOLE 24 | #define RESET "\033[0m" 25 | #define BLACK "\033[30m" /* Black */ 26 | #define RED "\033[31m" /* Red */ 27 | #define GREEN "\033[32m" /* Green */ 28 | #define YELLOW "\033[33m" /* Yellow */ 29 | #define BLUE "\033[34m" /* Blue */ 30 | #define MAGENTA "\033[35m" /* Magenta */ 31 | #define CYAN "\033[36m" /* Cyan */ 32 | #define WHITE "\033[37m" /* White */ 33 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 34 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 35 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 36 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 37 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 38 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 39 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 40 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 41 | #else 42 | #define RESET 43 | #define BLACK 44 | #define RED 45 | #define GREEN 46 | #define YELLOW 47 | #define BLUE 48 | #define MAGENTA 49 | #define CYAN 50 | #define WHITE 51 | #define BOLDBLACK 52 | #define BOLDRED 53 | #define BOLDGREEN 54 | #define BOLDYELLOW 55 | #define BOLDBLUE 56 | #define BOLDMAGENTA 57 | #define BOLDCYAN 58 | #define BOLDWHITE 59 | #endif 60 | 61 | #if E4DEBUG 62 | // TODO: file, line, assert text format 63 | #define E4ASSERT(C) \ 64 | if (!(C)) { \ 65 | e4::failf(BOLDRED "ASSERT:" RESET " %s:%d -- " BOLDYELLOW #C "\n" RESET, \ 66 | __FILE__, __LINE__); \ 67 | } 68 | #define E4ASSERT_GTE(A, B) \ 69 | if (A < B) { \ 70 | e4::failf(BOLDRED "ASSERT:" RESET " %s:%d -- " BOLDYELLOW #A \ 71 | " (%zu) is not gt-eq " #B " (%zu) \n" RESET, \ 72 | __FILE__, __LINE__, A, B); \ 73 | } 74 | #define E4IF_NODEBUG(X) 75 | 76 | #define E4TODO(T) \ 77 | e4::failf(BOLDYELLOW "TODO:" RESET " %s (%s:%d)\n", T, __FILE__, __LINE__); 78 | 79 | #define E4FAIL(T) \ 80 | e4::failf(BOLDRED "FAIL:" RESET " %s:%d -- " BOLDYELLOW "%s\n" RESET, \ 81 | __FILE__, __LINE__, T); 82 | 83 | // Choose a debug value in debug build 84 | #define E4CHOICE(DEBUG, NODEBUG) (DEBUG) 85 | #define E4LOG0(A) debug_printf(A); 86 | #define E4LOG1(A, V) debug_printf(A, V); 87 | #define E4LOG2(A, V1, V2) debug_printf(A, V1, V2); 88 | #define E4LOG3(A, V1, V2, V3) debug_printf(A, V1, V2, V3); 89 | #define E4LOG4(A, V1, V2, V3, V4) debug_printf(A, V1, V2, V3, V4); 90 | #else 91 | // 92 | // Release variants do nothing or print very compact reports 93 | // 94 | #define E4ASSERT(C) (void)(C) 95 | #define E4ASSERT_GTE(A, B) (void)(A), (void)(B) 96 | #define E4IF_NODEBUG(X) X 97 | #define E4TODO(T) e4::failf("TODO:%s\n", T); 98 | #define E4FAIL(T) e4::failf("FAIL:%s\n", T); 99 | 100 | // Choose a nodebug value in release 101 | #define E4CHOICE(DEBUG, NODEBUG) (NODEBUG) 102 | #define E4LOG0(A) (void)0 103 | #define E4LOG1(A, V) (void)0 104 | #define E4LOG2(A, V1, V2) (void)0 105 | #define E4LOG3(A, V1, V2, V3) (void)0 106 | #define E4LOG4(A, V1, V2, V3, V4) (void)0 107 | #endif 108 | 109 | } // ns e4 110 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/fs.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "e4platf/types.h" 11 | #include "e4std/string.h" 12 | #include "e4std/vector.h" 13 | 14 | namespace platf { 15 | namespace fs { 16 | 17 | using e4::Count; 18 | using e4::String; 19 | using e4::Vector; 20 | 21 | DECL_EXCEPTION(Filesystem) 22 | 23 | #if E4FEATURE_FS 24 | 25 | class File { 26 | private: 27 | FILE *f_ = nullptr; 28 | 29 | public: 30 | explicit File(const char *fn, const char *mode) { 31 | f_ = ::fopen(fn, mode); 32 | E4ASSERT(is_open()); 33 | } 34 | 35 | ~File() { close(); } 36 | 37 | bool is_open() const { return f_ != nullptr; } 38 | 39 | void close() { 40 | if (f_) { 41 | ::fclose(f_); 42 | } 43 | f_ = nullptr; 44 | } 45 | 46 | static e4::Box read_file(const char *fn); 47 | 48 | e4::Box read_file(); 49 | }; 50 | 51 | #endif // E4FEATURE_FS 52 | 53 | // If Filesystem feature is enabled: this will scan search paths and find 'fn' 54 | // Otherwise: Will check if the file is present in statically linked files 55 | e4::Box 56 | read(const Vector& search_paths, const char* fn); 57 | 58 | bool exists(String& path); 59 | } 60 | } // ns platf::fs 61 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/mem.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/conf.h" 8 | #include "e4std/ptr.h" 9 | #include "e4std/stuff.h" 10 | #include 11 | #include 12 | #include 13 | 14 | namespace platf { 15 | 16 | // ARM compilers have this keyword to specify unaligned memory pointer 17 | #if E4_ARM 18 | #define E4PACKED __packed 19 | #else 20 | #define E4PACKED 21 | #endif 22 | 23 | // 24 | // Endian Swap helpers 25 | // 26 | 27 | #if E4_BIG_ENDIAN 28 | constexpr uint16_t big_to_native(uint16_t x) { return x; } 29 | constexpr uint32_t big_to_native(uint32_t x) { return x; } 30 | constexpr uint64_t big_to_native(uint64_t x) { return x; } 31 | #else 32 | // GCC 4.3+ builtins 33 | constexpr uint16_t big_to_native(uint16_t x) { return __builtin_bswap16(x); } 34 | constexpr uint32_t big_to_native(uint32_t x) { return __builtin_bswap32(x); } 35 | constexpr uint64_t big_to_native(uint64_t x) { return __builtin_bswap64(x); } 36 | #endif 37 | 38 | // portable unaligned memory read 39 | template 40 | inline VALUE unaligned_read(const PTRTYPE* src) { 41 | E4PACKED const VALUE* src2 = reinterpret_cast(src); 42 | return *src2; 43 | } 44 | 45 | // portable unaligned memory read with an optional byte-swap 46 | template 47 | inline VALUE unaligned_read_big(const PTRTYPE* src) { 48 | E4PACKED const VALUE* src2 = reinterpret_cast(src); 49 | return big_to_native(*src2); 50 | } 51 | 52 | // 53 | // Sys allocator delivers memory using simple new calls, no special memory 54 | // handling is here 55 | // 56 | struct SystemAllocator { 57 | // Allocates a single object on general heap 58 | template 59 | static e4::UniquePtr 60 | alloc_one(Args&&... args) { 61 | return e4::UniquePtr (new Type(std::forward(args)...)); 62 | } 63 | 64 | // Allocates array on general heap 65 | template 66 | static e4::UniqueArrayPtr 67 | alloc_many(::size_t sz) { 68 | return e4::UniqueArrayPtr (new Type[sz]); 69 | } 70 | 71 | // Allocates array on general heap, raw: no constructors called 72 | template 73 | static e4::UniquePtr 74 | alloc_raw(::size_t sz) { 75 | auto mem = new uint8_t[sizeof(Type) * sz]; 76 | return e4::UniquePtr (reinterpret_cast(mem)); 77 | } 78 | }; 79 | 80 | } // ns platf 81 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/messages.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4platf/debug.h" 9 | 10 | namespace platferr { 11 | #define DEFERR(NAME, VAL) constexpr const char* NAME = VAL; 12 | 13 | DEFERR(r_data_exhausted, E4CHOICE("Reader: input data exhausted", "P:R1")) 14 | DEFERR(r_varint_too_long, E4CHOICE("Reader: varint is too long", "P:R2")) 15 | 16 | DEFERR(fs_notfound, E4CHOICE("File not found", "ENOENT")) 17 | 18 | #undef DEFERR 19 | } // ns platferr 20 | -------------------------------------------------------------------------------- /Runtime/include/e4platf/types.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/debug.h" 8 | #include "e4platf/mem.h" 9 | #include "e4std/array.h" 10 | #include "e4std/map.h" 11 | #include "e4std/sizes.h" 12 | #include "e4std/string.h" 13 | #include "e4std/stuff.h" 14 | #include "e4std/vector.h" 15 | #include 16 | #include 17 | #include 18 | 19 | namespace e4 { 20 | 21 | #if E4_WORD_SIZE == 64 22 | using Word = uint64_t; 23 | using SignedWord = int64_t; 24 | constexpr Word BYTES_PER_WORD = 8; 25 | #elif E4_WORD_SIZE == 32 26 | using Word = uint32_t; 27 | using SignedWord = int32_t; 28 | constexpr Word BYTES_PER_WORD = 4; 29 | #else 30 | #error E4_WORD_SIZE other than 32/64 not supported yet 31 | #endif 32 | constexpr Word BITS_PER_WORD = sizeof(Word) * 8; 33 | 34 | 35 | #if E4FEATURE_FLOAT 36 | using Float = double; 37 | #else 38 | using Float = Word; 39 | #endif 40 | 41 | 42 | // Ensure that casting to word will not lose any bits 43 | // TODO: Make this count bits instead of runtime checking the value 44 | template 45 | constexpr bool fits_in(ValueType i) { 46 | return static_cast(static_cast(i)) == i; 47 | } 48 | 49 | // count type 50 | using Count = Word; 51 | using SignedCount = SignedWord; 52 | 53 | // size types for byte and word arrays 54 | template 55 | using GenericSize = T_GenericSize; 56 | 57 | using ByteSize = T_GenericSize; 58 | 59 | using WordSize = T_GenericSize; // Word is same as Term 60 | 61 | template 62 | using Vector = std::vector; 63 | 64 | // Stupid vector which does not bother with constructing/destructing 65 | template 66 | using PODVector = std::vector; 67 | 68 | template 69 | using HashMap = std::unordered_map; 70 | 71 | template 72 | using HashMap2 = std::unordered_map; 73 | 74 | template 75 | using HashMap3 = std::unordered_map; 76 | 77 | using String = std::string; 78 | 79 | template 80 | using UniquePtr = std::unique_ptr; 81 | 82 | template 83 | using UniqueArrayPtr = std::unique_ptr; 84 | 85 | } // ns e4 86 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/atom_store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "e4std/map.h" 4 | #include "e4std/vector.h" 5 | 6 | #include "e4rt/term.h" 7 | #include "e4rt/term_as_map_key.h" 8 | 9 | namespace e4 { 10 | 11 | namespace impl { 12 | struct CStrHasher { 13 | std::size_t operator()(const char* k) const { 14 | return std::_Hash_bytes(k, std::strlen(k), 0x13131313); 15 | // size_t hash = 0; 16 | //#if E4_WORD_SIZE == 32 17 | // size_t seed = 0x13131313; 18 | //#elif E4_WORD_SIZE == 64 19 | // size_t seed = 0x1313131313131313UL; 20 | //#endif 21 | // while (*k) { 22 | // hash = (hash * seed) + (*k); 23 | // k++; 24 | // } 25 | // return hash; 26 | } 27 | }; 28 | 29 | struct CStrEqual: public std::binary_function { 30 | bool operator() (const char* a, const char *b) const { 31 | return std::strcmp(a, b) == 0; 32 | } 33 | }; 34 | } // ns impl 35 | 36 | 37 | // Stores short strings which never get freed in blocks which never move 38 | class AtomStore { 39 | private: 40 | constexpr static size_t BLOCK_SZ = 1024; 41 | 42 | Word atom_id_ = 0; 43 | 44 | Vector> blocks_; 45 | 46 | size_t block_remaining_ = 0; 47 | 48 | char* block_pos_ = nullptr; 49 | 50 | HashMap2 atom_to_str_; 51 | 52 | HashMap3 str_to_atom_; 53 | 54 | public: 55 | AtomStore() = default; 56 | 57 | size_t size() const { 58 | return atom_to_str_.size(); 59 | } 60 | 61 | const char* find_atom(Term atom) const; 62 | 63 | Term find_atom(const char* s) const; // returns NON_VALUE or an atom 64 | 65 | Term insert(const char* str); 66 | 67 | #if E4DEBUG 68 | void debug_print(); 69 | #else 70 | void debug_print() {} 71 | #endif 72 | 73 | private: 74 | // Copies s to blocks_ forever (or allocates a new block) 75 | const char* intern(const char* s); 76 | }; 77 | 78 | 79 | } // ns e4 80 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/binary.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4rt/box.h" 9 | #include "e4rt/heap.h" 10 | #include "e4rt/vm.h" 11 | 12 | namespace e4 { 13 | 14 | constexpr Word PROCBIN_THRESHOLD = 50; 15 | 16 | // A heap object representing an onheap binary (< threshold) 17 | class ProcBinaryBox { 18 | public: 19 | BoxHeaderWord header_; 20 | uint8_t data_[0]; 21 | }; 22 | 23 | // Refcounted binary on the separate heap 24 | class RCBinary { 25 | public: 26 | Word size_; 27 | Word rc_; // ref count 28 | uint8_t data_[0]; 29 | }; 30 | 31 | // A process heap object holding refcount to an external RCBinary 32 | class RCBinaryBox { 33 | public: 34 | BoxHeaderWord header_; 35 | RCBinary* bin_; 36 | }; 37 | 38 | template 39 | ProcBinaryBox* make_proc_binary(Heap& heap, GenericSize size) { 40 | E4LOG1("bin: new procbin %zu b\n", size.bytes()); 41 | E4ASSERT(size.bytes() <= PROCBIN_THRESHOLD); 42 | auto wsz = Heap::word_size(size); 43 | auto newbox = heap.allocate_box(wsz, BoxTag::ProcBinary, size.bytes()); 44 | auto pbin = reinterpret_cast(newbox); 45 | pbin->header_.set_tag(BoxTag::ProcBinary); 46 | pbin->header_.set_val(size.bytes()); 47 | return pbin; 48 | } 49 | 50 | 51 | template 52 | RCBinaryBox* make_rc_binary(GenericSize size) { 53 | E4LOG1("bin: new rcbin %zu b\n", size.bytes()); 54 | // Too large, we allocate an RCBinaryBox instead 55 | auto w_size = Heap::word_size(GenericSize(1)); 56 | auto newbox = 57 | vm()->binary_heap_.allocate_box(w_size, BoxTag::RCBinary, size.bytes()); 58 | auto rcbin = reinterpret_cast(newbox); 59 | rcbin->header_.set_tag(BoxTag::RCBinary); 60 | rcbin->header_.set_val(0); 61 | return rcbin; 62 | } 63 | 64 | } // ns e4 65 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/box.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4platf/types.h" 9 | 10 | #include "e4rt/term_tag.h" 11 | 12 | namespace e4 { 13 | 14 | namespace box_tag { 15 | typedef enum { 16 | Tuple, 17 | PositiveBignum, 18 | NegativeBignum, 19 | Float, 20 | Map, 21 | FunObject, 22 | Export, 23 | Pid, 24 | Port, 25 | Ref, 26 | DestroyedSomething, 27 | ProcBinary, 28 | RCBinary, 29 | MatchContext, 30 | SubBinary, 31 | } Type; 32 | } // ns box_tag; 33 | using BoxTag = box_tag::Type; 34 | 35 | class Term; 36 | 37 | // 38 | // First word of every boxed value is a header 39 | // 40 | class BoxHeaderWord { 41 | private: 42 | Word primary_tag_ : PRIMARY_TAG_BITS; 43 | 44 | Word tag_ : BOXED_TAG_BITS; // least-significant goes first 45 | 46 | Word val_ : BOXED_VALUE_BITS; 47 | 48 | PrimaryTag primary_tag() const { 49 | return static_cast(primary_tag_); 50 | } 51 | 52 | public: 53 | constexpr BoxHeaderWord(BoxTag t, Word val) 54 | : primary_tag_(static_cast(PrimaryTag::Header)), 55 | tag_(t), 56 | val_(val) { 57 | } 58 | 59 | void set_primary_tag(PrimaryTag pt) { 60 | primary_tag_ = static_cast(pt); 61 | } 62 | 63 | void set_tag(BoxTag t) { 64 | E4ASSERT(primary_tag() == PrimaryTag::Header); 65 | tag_ = t; 66 | } 67 | 68 | BoxTag tag() const { 69 | E4ASSERT(primary_tag() == PrimaryTag::Header); 70 | return static_cast(tag_); 71 | } 72 | 73 | void set_val(Word a) { 74 | E4ASSERT(primary_tag() == PrimaryTag::Header); 75 | val_ = a; 76 | } 77 | 78 | Word val() const { 79 | E4ASSERT(primary_tag() == PrimaryTag::Header); 80 | return val_; 81 | } 82 | 83 | // Given a pointer to whatever T* setup first word 84 | template 85 | static BoxHeaderWord* setup_a_box(T* memory, BoxTag bt, Word val) { 86 | auto hword = reinterpret_cast(memory); 87 | hword->set_primary_tag(PrimaryTag::Header); 88 | hword->set_tag(bt); 89 | hword->set_val(val); 90 | return hword; 91 | } 92 | }; 93 | 94 | static_assert(sizeof(BoxHeaderWord) == sizeof(Word), 95 | "BoxHeaderWord must have 1 word size"); 96 | 97 | // A tuple header 98 | class TupleBoxHeader { 99 | private: 100 | BoxHeaderWord header_; 101 | Word data_[0]; 102 | 103 | public: 104 | constexpr TupleBoxHeader(Word val) : header_(BoxTag::Tuple, val), data_() {} 105 | 106 | operator Term() const; // impl in term.cpp 107 | 108 | Word element(Word z_index) const { return data_[z_index]; } 109 | 110 | void set_element(Word z_index, Word val) { data_[z_index] = val; } 111 | 112 | BoxTag tag() const { return header_.tag(); } 113 | 114 | Word val() const { return header_.val(); } 115 | }; 116 | 117 | static_assert(sizeof(TupleBoxHeader) == sizeof(Word), 118 | "BoxHeader must have 1 word size"); 119 | 120 | } // ns e4 121 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/bytecode.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "e4platf/types.h" 9 | 10 | namespace e4 { 11 | 12 | namespace instr { 13 | typedef enum { 14 | FuncInfo = 0x01, 15 | Label = 0x02, // removed at load time 16 | LineInfo = 0x03, // removed at load time; UNUSED 17 | 18 | CallLocal = 0x10, 19 | CallLocalTail = 0x11, 20 | CallExt = 0x12, 21 | CallExtTail = 0x13, 22 | CallBif = 0x14, 23 | CallBifGc = 0x15, 24 | Ret0 = 0x16, 25 | RetN = 0x17, 26 | Jump = 0x18, 27 | SelectVal = 0x19, 28 | CallFun = 0x1A, 29 | 30 | Alloc = 0x20, 31 | GetElement = 0x21, 32 | Move = 0x22, 33 | SetNil = 0x23, 34 | PutTuple = 0x24, 35 | Put = 0x25, 36 | Cons = 0x26, 37 | Trim = 0x27, 38 | MakeFun = 0x28, 39 | SetElement = 0x29, 40 | ClearStack = 0x2A, // use setnil? 41 | TestHeap = 0x2B, 42 | } Type; 43 | } // instr 44 | using Instruction = instr::Type; 45 | 46 | 47 | // 48 | // A pointer to code, abstracting away memory access to be able to optimize 49 | // this later and avoid unaligned byte reads 50 | // 51 | class CodeAddress { 52 | const uint8_t *ptr_ = nullptr; 53 | static const uint8_t *BASE_ADDR; 54 | 55 | public: 56 | CodeAddress() = default; 57 | 58 | explicit CodeAddress(const uint8_t *p) : ptr_(p) {} 59 | 60 | static const uint8_t *get_base() { 61 | return BASE_ADDR; 62 | } 63 | 64 | const uint8_t *ptr() const { 65 | return ptr_; 66 | } 67 | 68 | uint8_t fetch() const { 69 | return *ptr_; 70 | } 71 | 72 | CodeAddress& operator += (Word s) { 73 | ptr_ += s; 74 | return *this; 75 | } 76 | 77 | CodeAddress& operator ++ () { 78 | ptr_++; 79 | return *this; 80 | } 81 | }; 82 | 83 | } // ns e4 84 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/code_mgr.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/conf.h" 8 | #include "e4platf/types.h" 9 | #include "e4rt/module.h" 10 | #include "e4rt/term.h" 11 | #include "e4rt/term_as_map_key.h" 12 | 13 | namespace e4 { 14 | 15 | class VM; 16 | 17 | class CodeManager { 18 | private: 19 | HashMap> mods_; 20 | 21 | Vector paths_; // Code search paths, starting with "." 22 | 23 | public: 24 | explicit CodeManager(): mods_() { 25 | // TODO: load preloaded modules 26 | paths_.push_back(String(".")); 27 | } 28 | 29 | Term load(Term name); 30 | 31 | void register_module(UniquePtr&& m); 32 | 33 | Module* find_module(Term name) const; 34 | 35 | void path_add(const String& p) { 36 | paths_.push_back(p); 37 | } 38 | 39 | #if E4DEBUG 40 | void debug_print(); 41 | #else 42 | void debug_print() {} 43 | #endif 44 | }; 45 | 46 | } // ns e4 47 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/dist.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4rt/term.h" 9 | 10 | namespace e4 { 11 | 12 | // This class has size of 2 words padded with 7 bytes because creation is a byte 13 | #ifdef __clang__ 14 | #pragma clang diagnostic push 15 | #pragma clang diagnostic ignored "-Wpadded" 16 | #endif 17 | 18 | // Erl dist node implementation 19 | class Node { 20 | public: 21 | Term ml_sysname = NIL; 22 | dist::Creation m_creation = dist::INTERNAL_CREATION; 23 | }; 24 | 25 | #ifdef __clang__ 26 | #pragma clang diagnostic pop 27 | #endif 28 | 29 | } // ns e4 30 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/ext_term_format.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4platf/byte_stream_reader.h" 9 | #include "e4rt/dist.h" 10 | #include "e4rt/heap.h" 11 | #include "e4rt/term.h" 12 | #include "e4rt/vm.h" 13 | 14 | namespace e4 { 15 | 16 | class ExtTerm { 17 | private: 18 | static constexpr Word ETF_MARKER = 131; 19 | 20 | enum class Tag : Word { 21 | DistHeader = 68, // contains atom cache 22 | // 69 23 | IeeeFloatExt = 70, // 8-byte double 24 | // ... 25 | BitBinaryExt = 77, 26 | // ... 27 | Compressed = 80, 28 | // 81 29 | AtomCacheRef = 82, // used with dist header 30 | // ... 31 | SmallIntegerExt = 97, // 8bit integer 32 | IntegerExt = 98, // 32bit big endian integer 33 | OldFloatStringExt = 99, // superceded by ieee_float_ext 34 | AtomExt = 100, // atom as string 35 | ReferenceExt = 101, // encoded make_ref() 36 | PortExt = 102, // port, similar to ref() 37 | PidExt = 103, 38 | SmallTupleExt = 104, 39 | LargeTupleExt = 105, 40 | NilExt = 106, // empty list [] 41 | StringExt = 107, // 16bit size + bytes 42 | ListExt = 108, // 32bit length, elements, tail (or nil) 43 | BinaryExt = 109, 44 | SmallBigExt = 110, 45 | LargeBigExt = 111, 46 | // NEW_FUN_EXT = 112, 47 | // EXPORT_EXT = 113, 48 | // NEW_REFERENCE_EXT = 114, 49 | SmallAtomExt = 115, 50 | MapExt = 116, 51 | // FUN_EXT = 117, 52 | AtomUtf8Ext = 118, 53 | SmallAtomUtf8Ext = 119, 54 | }; // enum 55 | 56 | static Term read_atom_string_i16(tool::Reader& r); 57 | static Term read_atom_string_i8(tool::Reader& r); 58 | static Term read_tagged_atom_string(tool::Reader& r); 59 | static Node* get_node(Term /*sysname*/, dist::Creation /*creation*/); 60 | static Term make_pid(Term sysname, 61 | Word id, 62 | Word serial, 63 | uint8_t creation); 64 | static Term read_tuple(Heap& heap, tool::Reader& r, Word arity); 65 | static Term read_string_ext(Heap& heap, tool::Reader& r); 66 | static Term read_list_ext(Heap& heap, tool::Reader& r); 67 | static Term read_binary(Heap& heap, tool::Reader& r); 68 | 69 | public: 70 | // Term will be stored on heap (reads byte=131 first as an ETF tag) 71 | static Term read_with_marker(Heap& heap, tool::Reader& r); 72 | 73 | // Term will be stored on heap (reads type tag first) 74 | static Term read(Heap& heap, tool::Reader& r); 75 | }; 76 | 77 | } // ns e4 78 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/heap.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4std/ptr.h" 9 | 10 | #include "e4rt/box.h" 11 | #include "e4rt/term.h" 12 | 13 | namespace e4 { 14 | 15 | // A growable heap to hold Erlang terms 16 | class Heap { 17 | private: 18 | Word capacity_; 19 | Word htop_; 20 | UniquePtr heap_; 21 | 22 | public: 23 | explicit Heap(Word init_size) : capacity_(0), htop_(0) { 24 | this->alloc_heap(init_size); 25 | } 26 | 27 | bool have(Word want_size) const { return (capacity_ - htop_) >= want_size; } 28 | 29 | bool empty() const { return htop_ == 0; } 30 | 31 | E4_NODISCARD ConsCell* allocate_cons() { 32 | E4LOG0("heap: alloc cons\n"); 33 | return reinterpret_cast(allocate_raw_words(2)); 34 | } 35 | 36 | // A helper to find any size in words 37 | template 38 | static WordSize word_size(GenericSize sz) { 39 | return WordSize(sz.template as_units()); 40 | } 41 | 42 | // Create a box in memory, set up its first word with boxtag and value bits 43 | E4_NODISCARD BoxHeaderWord* allocate_box(WordSize want_size, 44 | BoxTag bt, 45 | Word val) { 46 | E4LOG1("heap: alloc box %zu w\n", want_size.units()); 47 | Word* box = allocate_raw_words(want_size.units() + 1); 48 | return BoxHeaderWord::setup_a_box(box, bt, val); 49 | } 50 | 51 | E4_NODISCARD TupleBoxHeader* allocate_tuple_box(Word arity) { 52 | return reinterpret_cast( 53 | allocate_box(WordSize(arity), BoxTag::Tuple, arity)); 54 | } 55 | 56 | private: 57 | Word* allocate_raw_words(Word want_size) { 58 | if (not have(want_size)) { 59 | // Grow or something 60 | E4TODO("Grow or something"); 61 | return nullptr; 62 | } 63 | auto result = heap_.get() + htop_; 64 | htop_ += want_size; 65 | E4LOG3("h::alloc(%zu) htop %zu size %zu\n", want_size, htop_, capacity_); 66 | return result; 67 | } 68 | 69 | void alloc_heap(Word size) { 70 | heap_ = platf::SystemAllocator::alloc_raw(size); 71 | capacity_ = size; 72 | } 73 | }; 74 | 75 | } // ns e4 76 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/messages.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/debug.h" 8 | 9 | namespace e4err { 10 | #define DEF_ERR(NAME, VAL) constexpr const char* NAME = VAL; 11 | 12 | DEF_ERR(no_feat_bignum, E4CHOICE("Bignum feature is disabled", "F:Big")) 13 | DEF_ERR(no_feat_maps, E4CHOICE("Maps feature is disabled", "F:Map")) 14 | DEF_ERR(no_feat_float, E4CHOICE("Float feature is disabled", "F:Flt")) 15 | DEF_ERR(no_feat_erldist, 16 | E4CHOICE("Erlang Distribution feature is disabled", "F:Dist")) 17 | 18 | DEF_ERR(etf_bad_tag, 19 | E4CHOICE("Bad tag encountered while reading ext term", "E:!Tag")) 20 | DEF_ERR(etf_bad_pid, E4CHOICE("Bad pid while reading ext term", "E:!Pid")) 21 | DEF_ERR(etf_atom_expected, E4CHOICE("Atom is expected", "E:Atom?")) 22 | 23 | DEF_ERR(mod_not_found, E4CHOICE("Module not found", "M:NX")) 24 | DEF_ERR(proc_not_found, E4CHOICE("Process pid not found", "P:NX")) 25 | 26 | DEF_ERR(code_undef, E4CHOICE("Function is not found or not exported", 27 | "C:Undef")) 28 | 29 | #undef DEF_ERR 30 | } // ns e4err 31 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/module.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/debug.h" 8 | #include "e4platf/types.h" 9 | 10 | #include "e4rt/bytecode.h" 11 | #include "e4rt/heap.h" 12 | #include "e4rt/module.h" 13 | #include "e4rt/term.h" 14 | 15 | #include "e4std/view.h" 16 | 17 | namespace e4 { 18 | 19 | class VM; 20 | //using e4::ByteView; 21 | 22 | // Element in exports table. Used to find functions referred by {M,F,Arity} 23 | // from the outside 24 | class Export { 25 | private: 26 | Term fun_; 27 | 28 | Arity arity_; 29 | 30 | Word offset_; // where the code begins 31 | 32 | public: 33 | explicit Export() : Export(NON_VALUE, Arity {0}, 0) {} 34 | 35 | Export(Term f, Arity a, Word offs) 36 | : fun_(f), arity_(a), offset_(offs) { 37 | } 38 | 39 | const Term &get_fun() const { 40 | return fun_; 41 | } 42 | 43 | Word get_offset() const { 44 | return offset_; 45 | } 46 | 47 | const Arity &get_arity() const { 48 | return arity_; 49 | } 50 | 51 | // Compares two exports as void* vs void*, returns -1 if ab, or 0 52 | static int compare_pvoid(const void *a, const void *b); 53 | 54 | static bool compare_less_pvoid(const void *a, const void *b) { 55 | return compare_pvoid(a, b) < 0; 56 | } 57 | 58 | bool operator == (const Export& other) const { 59 | return fun_.raw_equal(other.fun_) && arity_ == other.arity_; 60 | } 61 | 62 | #if E4DEBUG 63 | void print() const; 64 | #else 65 | void print() const {} 66 | #endif // DEBUG 67 | }; 68 | 69 | 70 | class Import { 71 | private: 72 | Term mod_; 73 | Term fun_; 74 | Arity arity_; 75 | 76 | public: 77 | const Term &get_mod() const { 78 | return mod_; 79 | } 80 | 81 | const Term &get_fun() const { 82 | return fun_; 83 | } 84 | 85 | const Arity &get_arity() const { 86 | return arity_; 87 | } 88 | 89 | Import(Term m, Term f, Arity a) 90 | : mod_(m), fun_(f), arity_(a) { 91 | } 92 | }; 93 | 94 | 95 | // A list of pairs [value | label] to use in select_val jumps 96 | class JumpTable { 97 | private: 98 | using Pair = std::tuple; 99 | Vector pairs_; 100 | 101 | public: 102 | JumpTable(Word capacity) { 103 | pairs_.reserve(capacity); 104 | } 105 | 106 | void push_back(Term t, Word label) { 107 | pairs_.emplace_back(t, label); 108 | } 109 | }; 110 | 111 | 112 | // Loader state to pass as argument to those who need it 113 | class ModuleLoaderState { 114 | private: 115 | Word atom_i_ = 0; 116 | 117 | UniquePtr atoms_; // atoms table from the module file, for lookups 118 | 119 | public: 120 | void reserve_atoms(size_t count) { 121 | atoms_ = platf::SystemAllocator::alloc_raw(count); 122 | } 123 | 124 | void add_atom(Term a) { 125 | auto aptr = atoms_.get(); 126 | aptr[atom_i_++] = a; 127 | } 128 | 129 | Term get_atom(size_t i) const { 130 | E4ASSERT(atom_i_ > i); 131 | auto aptr = atoms_.get(); 132 | return aptr[i]; 133 | } 134 | }; 135 | 136 | constexpr Word BAD_LABEL = ~0UL; 137 | 138 | // Captures things private for a module, groups them for convenient passing 139 | // to those who might need it 140 | class ModuleEnv { 141 | protected: 142 | friend class Module; 143 | 144 | Word literals_count_ = 0; 145 | UniquePtr literals_; 146 | 147 | Heap literal_heap_; 148 | 149 | Word exports_count_ = 0; 150 | UniquePtr exports_; 151 | 152 | Word imports_count_ = 0; 153 | UniquePtr imports_; 154 | 155 | Vector jump_tables_; 156 | 157 | PODVector labels_; 158 | 159 | public: 160 | explicit ModuleEnv(): literal_heap_(64) { 161 | } 162 | 163 | Word get_label(size_t i) const { 164 | if (not i) { 165 | return BAD_LABEL; 166 | } 167 | return labels_[i - 1]; 168 | } 169 | 170 | const Term &get_literal(size_t i) const { 171 | return literals_.get()[i]; 172 | } 173 | 174 | const Export &get_export(size_t i) const { 175 | return exports_.get()[i]; 176 | } 177 | 178 | const Import &get_import(size_t i) const { 179 | return imports_.get()[i]; 180 | } 181 | }; 182 | 183 | 184 | class Module { 185 | private: 186 | Term name_ = NON_VALUE; // atom name 187 | 188 | UniquePtr code_; 189 | 190 | ModuleEnv env_; 191 | 192 | public: 193 | explicit Module() : env_() {} 194 | 195 | void load(const BoxView& data); 196 | 197 | Term get_name() const { 198 | return name_; 199 | } 200 | 201 | Export* find_export(const MFArity& mfa) const; 202 | 203 | // Adds code start to export offset 204 | CodeAddress get_export_address(const Export& exp) const; 205 | 206 | private: 207 | void load_literals(const BoxView& adata); 208 | 209 | void load_exports(const BoxView& adata, 210 | const ModuleLoaderState& lstate); 211 | 212 | void load_imports(const BoxView& adata, 213 | const ModuleLoaderState& lstate); 214 | 215 | void load_atoms_section(const BoxView& section_view, 216 | MUTABLE ModuleLoaderState& lstate); 217 | 218 | void load_labels(const BoxView& adata, 219 | MUTABLE ModuleLoaderState& lstate); 220 | 221 | void load_jump_tables(const BoxView& adata, 222 | const ModuleLoaderState& lstate); 223 | }; 224 | 225 | } // ns e4 226 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/process.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4rt/bytecode.h" 8 | #include "e4rt/range_checker.h" 9 | #include "e4rt/term.h" 10 | #include "e4rt/vm.h" 11 | #include "e4std/complicated.h" 12 | 13 | namespace e4 { 14 | using e4::Error; 15 | 16 | 17 | // Stack implementation 18 | // TODO: stack base pointer to address args and variables together 19 | // TODO: some generic stack frame implementation for enter/leave. Dynamic heap 20 | // frames? 21 | class Stack { 22 | private: 23 | PODVector cells_; 24 | 25 | public: 26 | Stack() = default; 27 | // Will grow using vector realloc 28 | void push_term(Term t) { push(t.get_raw()); } 29 | 30 | void push(Word w) { cells_.push_back(w); } 31 | 32 | // Will shrink size but not shrink memory 33 | Word pop() { 34 | E4ASSERT(not cells_.empty()); 35 | auto val = cells_.back(); 36 | cells_.resize(cells_.size() - 1); 37 | return val; 38 | } 39 | 40 | Term pop_term() { return Term(pop()); } 41 | }; 42 | 43 | 44 | constexpr Word INIT_PROCESS_HEAP = 64; // first size for process heap (words) 45 | 46 | 47 | // VM runtime context which gets swapped into VM loop and out 48 | class RuntimeContext { 49 | public: 50 | CodeAddress pc_; 51 | Stack stack_; // data stack 52 | RangeChecker range_checker_; 53 | 54 | explicit RuntimeContext(const RangeChecker& rc) : range_checker_(rc) {} 55 | }; 56 | 57 | 58 | enum class ProcessPriority : Word { 59 | Normal, 60 | High 61 | }; 62 | 63 | 64 | class Process { 65 | private: 66 | Term pid_; 67 | 68 | Heap heap_; 69 | 70 | // [pid()] -- linked processes 71 | // Term links_ = NIL; 72 | // [pid()] -- processes which monitor this process 73 | // Term monitors_ = NIL; 74 | 75 | ProcessPriority prio_ = ProcessPriority::Normal; 76 | 77 | public: 78 | RuntimeContext context_; 79 | 80 | public: 81 | Process() = delete; 82 | 83 | explicit Process(Term pid); 84 | 85 | Term self() const { return pid_; } 86 | 87 | ProcessPriority priority() const { return prio_; } 88 | 89 | // Sets arguments and enters mfarity with args, does not wait for execution 90 | // but just sets instruction pointer instead 91 | E4_NODISCARD Error apply(const MFArgs& mfargs); 92 | 93 | // TODO: maybe belongs to runtime context 94 | void jump(CodeAddress newpc) { 95 | E4LOG1("[proc] jump %p\n", newpc.ptr()); 96 | context_.pc_ = newpc; 97 | } 98 | }; 99 | 100 | } // ns e4 101 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/range_checker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace e4 { 7 | 8 | class RangeChecker { 9 | const uint8_t* code_range_; 10 | const uint8_t* code_range_end_; 11 | 12 | public: 13 | explicit RangeChecker(const uint8_t* code_range, 14 | const uint8_t* code_range_end) 15 | : code_range_(code_range), code_range_end_(code_range_end) {} 16 | RangeChecker(const RangeChecker& other) = default; 17 | 18 | bool in_range(const uint8_t* p) const { 19 | return p >= code_range_ && p <= code_range_end_; 20 | } 21 | void assert_in_range(const uint8_t* p) const { E4ASSERT(in_range(p)); } 22 | }; 23 | 24 | } // ns e4 25 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "e4platf/types.h" 4 | 5 | #include "e4rt/term.h" 6 | 7 | namespace e4 { 8 | 9 | class Process; 10 | constexpr Word SCHED_HIGH_ADVANTAGE = 3; 11 | 12 | class Scheduler { 13 | private: 14 | // Queue pointers point to the last process scheduled (mod queue size) 15 | Word q_ptr_normal_ = 0; 16 | 17 | Word q_ptr_high_ = 0; 18 | 19 | PODVector runq_normal_; 20 | 21 | PODVector runq_high_; 22 | 23 | // One normal prio process is scheduled for every SCHED_HIGH_ADVANTAGE 24 | // cycles of high prio processes 25 | Word high_advantage_ = 0; 26 | 27 | Word pid_counter_ = 0; 28 | 29 | HashMap processes_; 30 | 31 | public: 32 | Scheduler() {} 33 | 34 | Term make_pid(); 35 | 36 | void register_proc(Process* p); 37 | 38 | void schedule(Process *p); 39 | 40 | // Take next process from the runqueue with respect to priorities 41 | Process *next(); 42 | 43 | private: 44 | Process *next_normal(); 45 | 46 | Process *next_high(); 47 | }; 48 | 49 | } // ns e4 50 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/term_as_map_key.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | // 8 | // Include this header to be able to use Term as a key for e4::Map 9 | // 10 | 11 | #include "e4rt/term.h" 12 | #include "e4std/map.h" 13 | 14 | namespace e4 { 15 | 16 | template <> 17 | inline bool compare_equal(const e4::Term a, const e4::Term b) { 18 | return a.get_raw() == b.get_raw(); 19 | } 20 | 21 | template <> 22 | inline bool compare_less(const e4::Term a, const e4::Term b) { 23 | return a.get_raw() < b.get_raw(); 24 | } 25 | 26 | } // ns e4 27 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/term_tag.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4platf/types.h" 9 | 10 | namespace e4 { 11 | 12 | // primary tag bits 13 | constexpr Word PRIMARY_TAG_BITS = 2; 14 | constexpr Word PRIMARY_VALUE_BITS = BITS_PER_WORD - PRIMARY_TAG_BITS; 15 | constexpr Word PRIMARY_VALUE_MASK = (~0ULL) << PRIMARY_TAG_BITS; 16 | 17 | enum class PrimaryTag : Word { 18 | Header = 0, 19 | Cons = 1, 20 | Boxed = 2, 21 | Immediate = 3, 22 | }; // ns primary_tag 23 | 24 | enum class Immed1Tag { 25 | Pid = 0, 26 | Port = 1, 27 | Immed2 = 2, 28 | Small = 3 29 | }; 30 | 31 | enum class Immed2Tag { 32 | Atom = 0, 33 | Catch = 1, 34 | Immed3 = 2, 35 | Special = 3 36 | }; 37 | 38 | enum class Immed3Tag { 39 | XReg = 0, 40 | YReg = 1, 41 | Label = 2, 42 | FloatReg = 3 43 | }; 44 | 45 | constexpr Word IMM1_TAG_BITS = 2; 46 | constexpr Word IMM1_VALUE_BITS = PRIMARY_VALUE_BITS - IMM1_TAG_BITS; 47 | constexpr Word IMM1_MAX_VALUE = (1UL << IMM1_VALUE_BITS) - 1; 48 | 49 | constexpr Word IMM2_TAG_BITS = 2; 50 | constexpr Word IMM2_VALUE_BITS = IMM1_VALUE_BITS - IMM2_TAG_BITS; 51 | constexpr Word IMM2_MAX_VALUE = (1UL << IMM2_VALUE_BITS) - 1; 52 | 53 | constexpr Word IMM3_TAG_BITS = 2; 54 | constexpr Word IMM3_VALUE_BITS = IMM2_VALUE_BITS - IMM3_TAG_BITS; 55 | constexpr Word IMM3_MAX_VALUE = (1UL << IMM3_VALUE_BITS) - 1; 56 | 57 | static constexpr Word BOXED_TAG_BITS = 4; 58 | static constexpr Word BOXED_VALUE_BITS = PRIMARY_VALUE_BITS - BOXED_TAG_BITS; 59 | 60 | } // ns e4 61 | -------------------------------------------------------------------------------- /Runtime/include/e4rt/vm.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include "e4rt/atom_store.h" 9 | #include "e4rt/code_mgr.h" 10 | #include "e4rt/dist.h" 11 | #include "e4rt/range_checker.h" 12 | #include "e4rt/scheduler.h" 13 | #include "e4std/array.h" 14 | #include "e4std/string.h" 15 | #include "process.h" 16 | 17 | namespace e4 { 18 | 19 | DECL_EXCEPTION(FeatureMissing) 20 | DECL_EXCEPTION(CodeLoader) 21 | DECL_EXCEPTION(Scheduler) 22 | DECL_EXCEPTION(CodeServer) 23 | DECL_EXCEPTION(Process) 24 | 25 | class Process; 26 | using e4::ArrayRef; 27 | 28 | // Erlang-Forth Abstract Machine (E4VM) 29 | class VM { 30 | private: 31 | Node* this_node_ = nullptr; 32 | 33 | Scheduler sched_; 34 | 35 | public: 36 | static VM* singleton_; 37 | 38 | AtomStore atom_store_; 39 | 40 | CodeManager modules_; 41 | 42 | Heap binary_heap_; 43 | 44 | RangeChecker range_checker_; 45 | 46 | explicit VM() 47 | : modules_(), 48 | binary_heap_(1024), 49 | range_checker_(nullptr, nullptr) 50 | { 51 | singleton_ = this; 52 | } //-V730 53 | 54 | void run(); 55 | 56 | const RangeChecker& get_code_range_checker() { return range_checker_; } 57 | 58 | // 59 | // Atom storage stuff 60 | // 61 | Term add_atom(const char* atom_name); 62 | 63 | const char* find_atom(Term atom) const; 64 | 65 | Node* dist_this_node(); 66 | 67 | // 68 | // Process and pid stuff 69 | // 70 | Process* spawn(Term parent_pid, const MFArgs& mfargs); 71 | 72 | #if E4DEBUG 73 | 74 | void print(Term t) const; 75 | 76 | void print_imm(Term t) const; 77 | // void print_atoms() const; 78 | #endif 79 | 80 | void print_imm_imm3(const Term& t) const; 81 | 82 | void print_imm_imm2(const Term& t) const; 83 | }; 84 | 85 | inline VM* vm() { return VM::singleton_; } 86 | 87 | } // ns e4 88 | -------------------------------------------------------------------------------- /Runtime/include/e4std/array.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | // 8 | // A custom, badly written replacement for C++ Array 9 | // 10 | 11 | #include 12 | 13 | namespace e4 { 14 | 15 | template 16 | class Array {}; 17 | 18 | // A non-owning pointer with size (element count) 19 | template 20 | class ArrayRef { 21 | private: 22 | ValueType* ptr_; 23 | ::size_t count_; 24 | 25 | public: 26 | ArrayRef() = default; 27 | ArrayRef(ValueType* p, ::size_t count) : ptr_(p), count_(count) {} 28 | 29 | ::size_t count() const { return count_; } 30 | const ValueType* first() const { return ptr_; } 31 | ValueType* first() { return ptr_; } 32 | }; 33 | 34 | } // ns e4 35 | -------------------------------------------------------------------------------- /Runtime/include/e4std/complicated.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | namespace e4 { 8 | 9 | class Error { 10 | const char* what_ = nullptr; 11 | 12 | explicit Error(const char* w) : what_(w) {} 13 | 14 | public: 15 | static Error success() { return Error(nullptr); } 16 | 17 | static Error fail(const char* w) { return Error(w); } 18 | 19 | bool is_success() const { return what_ == nullptr; } 20 | 21 | bool is_fail() const { return what_ != nullptr; } 22 | 23 | const char* get_fail() const { return what_; } 24 | 25 | void assert_success() const { 26 | #if E4DEBUG 27 | if (is_fail()) { 28 | E4ASSERT(what_); 29 | e4::failf(RED "%s" RESET "\n", what_); 30 | } 31 | #endif 32 | } 33 | }; 34 | 35 | // template 36 | // class Maybe { 37 | // ValueType val_; 38 | // bool have_value_ = false; 39 | // public: 40 | //}; 41 | 42 | } // ns e4 43 | -------------------------------------------------------------------------------- /Runtime/include/e4std/free_fun.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace e4 { 10 | 11 | // C style C++ constructor without args: constructs a class of some type in dst 12 | using CtorFun0 = void (*)(void* dst); 13 | // C style C++ destructor, calls ~Class on dst 14 | using DtorFun = void (*)(void* dst); 15 | 16 | // Override this or use this as a function pointer in generic functions which 17 | // have to construct different types 18 | template 19 | void destruct(Type* t) { 20 | t->~Type(); 21 | } 22 | 23 | // Override this or use this as a function pointer in generic functions which 24 | // have to construct different types 25 | template 26 | void construct_in(Type* t) { 27 | *t = Type(); 28 | } 29 | 30 | // Override this for your beautiful key type 31 | template 32 | bool compare_equal(Type a, Type b) { 33 | return a == b; 34 | } 35 | template <> 36 | bool compare_equal(const char* a, const char* b); 37 | 38 | // Override this for your beautiful key type 39 | template 40 | bool compare_less(const Type a, const Type b) { 41 | return a < b; 42 | } 43 | template <> 44 | bool compare_less(const char* a, const char* b); 45 | 46 | // C style comparison function which returns -1 if ab else 0 47 | using VoidpCompareFun = int (*)(const void* a, const void* b); 48 | 49 | } // ns e4 50 | -------------------------------------------------------------------------------- /Runtime/include/e4std/map.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | // 7 | // A custom, badly written replacement for C++ Map (using < for node ordering) 8 | // 9 | 10 | #include 11 | #include "e4std/free_fun.h" 12 | 13 | namespace e4 { 14 | 15 | #if 0 16 | 17 | template 18 | struct MapNode { 19 | KeyType key_; 20 | ValueType value_; 21 | MapNode* left_; 22 | MapNode* right_; 23 | 24 | explicit MapNode(const KeyType& k, const ValueType& v) 25 | : key_(k), value_(v), left_(), right_() {} 26 | 27 | MapNode(const KeyType& k, const ValueType& v, MapNode* left, MapNode* right) 28 | : key_(k), value_(v), left_(left), right_(right) {} 29 | }; 30 | 31 | template 34 | class Map { 35 | public: 36 | using NodeType = MapNode; 37 | Map() : root_() {} 38 | 39 | private: 40 | NodeType* root_; 41 | 42 | public: 43 | void insert(const KeyType& key, const ValueType& val) { 44 | if (root_) { 45 | insert_helper(root_, key, val); 46 | } else { 47 | // TODO: use alloc class in platf/mem.h 48 | root_ = Allocator::template alloc_one(key, val); 49 | } 50 | } 51 | 52 | ::size_t size() const { return count_nodes_helper(root_); } 53 | 54 | ::size_t depth() const { return depth_helper(this->root_); } 55 | 56 | using VisitorFun = void (*)(const void* k, const void* v, void* extra); 57 | void visit_nodes(VisitorFun fn, void* extra) const { 58 | return visit_nodes_helper(root_, fn, extra); 59 | } 60 | 61 | bool remove(const KeyType& key) { return remove_helper(nullptr, root_, key); } 62 | 63 | NodeType* find(const KeyType& key) const { return find_helper(root_, key); } 64 | 65 | private: 66 | void insert_helper(NodeType* root, const KeyType& key, const ValueType& val) { 67 | if (compare_less(key, root->key_)) { 68 | if (not root->left_) { 69 | root->left_ = Allocator::template alloc_class(key, val); 70 | } else { 71 | insert_helper(root->left_, key, val); 72 | } 73 | } else { 74 | if (not root->right_) { 75 | root->right_ = Allocator::template alloc_class(key, val); 76 | } else { 77 | insert_helper(root->right_, key, val); 78 | } 79 | } 80 | } 81 | 82 | ::size_t count_nodes_helper(const NodeType* root) const { 83 | if (not root) { 84 | return 0; 85 | } else { 86 | return 1 + count_nodes_helper(root->left_) + 87 | count_nodes_helper(root->right_); 88 | } 89 | } 90 | 91 | void visit_nodes_helper(const NodeType* root, 92 | VisitorFun eachfn, 93 | void* extra) const { 94 | if (not root) { 95 | return; 96 | } else { 97 | eachfn(&root->key_, &root->value_, extra); 98 | visit_nodes_helper(root->left_, eachfn, extra); 99 | visit_nodes_helper(root->right_, eachfn, extra); 100 | } 101 | } 102 | 103 | ::size_t depth_helper(const NodeType* root) const { 104 | if (not root) { 105 | return 0; 106 | } else { 107 | return 1 + max(depth_helper(root->left_), depth_helper(root->right_)); 108 | } 109 | } 110 | 111 | bool remove_helper(NodeType* parent, NodeType* current, const KeyType& key) { 112 | if (not current) { 113 | return false; 114 | } 115 | if (compare_equal(current->key_, key)) { 116 | if (not current->left_ || not current->right_) { 117 | NodeType* t = current->left_; 118 | if (current->right_) { 119 | t = current->right_; 120 | } 121 | if (parent) { 122 | if (parent->left_ == current) { 123 | parent->left_ = t; 124 | } else { 125 | parent->right_ = t; 126 | } 127 | } else { 128 | this->root_ = t; 129 | } 130 | } else { 131 | NodeType* valid_sub = current->right_; 132 | while (valid_sub->left_) { 133 | valid_sub = valid_sub->left_; 134 | } 135 | KeyType u = current->key_; 136 | current->key_ = valid_sub->key_; 137 | valid_sub->key_ = u; 138 | return remove_helper(current, current->right_, u); 139 | } 140 | Allocator::free(current); 141 | return true; 142 | } 143 | 144 | if (compare_less(key, current->key_)) { 145 | return remove_helper(current, current->left_, key); 146 | } else { 147 | return remove_helper(current, current->right_, key); 148 | } 149 | } 150 | 151 | NodeType* find_helper(NodeType* current, KeyType key) const { 152 | if (not current) { 153 | return nullptr; 154 | } 155 | if (compare_equal(current->key_, key)) { 156 | return current; 157 | } 158 | 159 | if (compare_less(key, current->key_)) { 160 | return find_helper(current->left_, key); 161 | } else { 162 | return find_helper(current->right_, key); 163 | } 164 | } 165 | }; 166 | #endif //0 167 | 168 | } // ns e4 169 | -------------------------------------------------------------------------------- /Runtime/include/e4std/ptr.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #pragma once 6 | 7 | #include "e4platf/mem.h" 8 | #include "e4platf/mem.h" 9 | #include "e4std/stuff.h" 10 | #include 11 | #include 12 | #include 13 | 14 | namespace e4 { 15 | 16 | template using UniquePtr = std::unique_ptr; 17 | template using UniqueArrayPtr = std::unique_ptr; 18 | 19 | 20 | // 21 | // Owns smart pointer to own memory and has a size 22 | // 23 | template 24 | class Box { 25 | private: 26 | UniquePtr p_; 27 | size_t count_; 28 | 29 | public: 30 | Box(Type* p, size_t c): p_(p), count_(c) { 31 | } 32 | 33 | Box(UniquePtr && p, size_t c): p_(std::move(p)), count_(c) { 34 | } 35 | 36 | Type* get() { 37 | return p_.get(); 38 | } 39 | 40 | const Type* get() const { 41 | return p_.get(); 42 | } 43 | 44 | size_t size() const { 45 | return count_; 46 | } 47 | 48 | // Iterator interface 49 | const Type *cbegin() const { 50 | return p_.get(); 51 | } 52 | 53 | const Type *cend() const { 54 | return cbegin() + count_; 55 | } 56 | }; 57 | 58 | 59 | // 60 | // A size-delimited data block which does not own its data. 61 | // Views point to read-only data. 62 | // 63 | template 64 | class BoxView { 65 | private: 66 | const T* data_; 67 | 68 | size_t size_; 69 | 70 | public: 71 | BoxView(const BoxView& other) = default; 72 | 73 | explicit BoxView() : data_(nullptr), size_(0) {} 74 | 75 | explicit BoxView(const T* data, size_t s) 76 | : data_(data), size_(s) {} 77 | 78 | explicit BoxView(const Box& vec) 79 | : data_(vec.get()), size_(vec.size()) {} 80 | 81 | bool is_empty() const { return not data_ || not size_; } 82 | 83 | size_t size() const { return size_; } 84 | 85 | // Iterator interface 86 | const T* cbegin() const { return data_; } 87 | 88 | const T* cend() const { return cbegin() + size_; } 89 | 90 | static BoxView view(const Box& box) { 91 | return BoxView(box.get(), box.size()); 92 | } 93 | }; 94 | 95 | 96 | } // ns e4 97 | -------------------------------------------------------------------------------- /Runtime/include/e4std/sizes.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace e4 { 11 | 12 | // Generic size type which respects units 13 | template 14 | class T_GenericSize { 15 | private: 16 | static constexpr ::size_t UNIT_SIZE = sizeof(StoredType); 17 | // stores amount in units (UNIT_SIZE), multiply when bytes are requested 18 | StorageType units_; 19 | 20 | public: 21 | T_GenericSize(const T_GenericSize& other) 22 | : units_(other.units_) {} 23 | 24 | explicit T_GenericSize(::size_t n) : units_(n) {} 25 | 26 | // Returns byte size to store this count of units 27 | StorageType bytes() const { return units_ * UNIT_SIZE; } 28 | 29 | // Returns unit count as is 30 | StorageType units() const { return units_; } 31 | 32 | // Recalculates how many units of AS_UNIT_SIZE would fit into this size 33 | template 34 | StorageType as_units() const { 35 | return (bytes() + sizeof(OtherType) - 1) / sizeof(OtherType); 36 | } 37 | }; 38 | 39 | } // ns e4 40 | -------------------------------------------------------------------------------- /Runtime/include/e4std/string.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | // 8 | // A custom, badly written replacement for C++ String/C string wrapper 9 | // 10 | #include "e4std/ptr.h" 11 | #include "e4std/stuff.h" 12 | #include "e4std/vector.h" 13 | 14 | #include "e4platf/debug.h" 15 | 16 | #include 17 | 18 | namespace e4 { 19 | 20 | using T_String = std::string; 21 | #if 0 22 | class String { 23 | private: 24 | Vector content_; 25 | 26 | public: 27 | String() { clear(); } 28 | 29 | explicit String(const char* src) { *this = src; } 30 | 31 | // Move ctor 32 | String(String&& mv) { content_ = std::move(mv.content_); } 33 | 34 | void clear() { 35 | content_.clear(); 36 | content_.push_back('\0'); 37 | } 38 | 39 | // Move assignment 40 | String& operator=(String&& other) { 41 | content_ = std::move(other.content_); 42 | return *this; 43 | } 44 | 45 | // Copy assignment 46 | String& operator=(const String& other) { 47 | content_.resize(other.size() + 1); // extra for trailing '\0' 48 | if (content_.data() && other.content_.data()) { 49 | E4ASSERT(content_.capacity() >= other.content_.size() + 1); 50 | ::memcpy(content_.data(), other.content_.data(), other.content_.size()); 51 | } 52 | return *this; 53 | } 54 | 55 | // Copy ctor 56 | explicit String(const String& other) { *this = other; } 57 | 58 | String(const char* src, ::size_t sz) { 59 | content_.resize(sz + 1); 60 | auto datap = content_.data(); 61 | ::memcpy(datap, src, sz); 62 | datap[sz] = '\0'; 63 | } 64 | 65 | ::size_t size() const { return content_.size() + 1; } 66 | 67 | ::size_t capacity() const { return content_.capacity() - 1; } 68 | 69 | String& operator=(const char* src) { 70 | auto srclen = ::strlen(src); 71 | content_.resize(srclen + 1); 72 | 73 | auto datap = content_.data(); 74 | ::memcpy(datap, src, srclen); 75 | datap[srclen] = '\0'; 76 | 77 | return *this; 78 | } 79 | 80 | String operator+(const char* str) const { 81 | String tmp(*this); 82 | tmp.resize(size() + ::strlen(str)); 83 | ::strcat(tmp.data(), str); 84 | return tmp; 85 | } 86 | 87 | const char* data() const { return content_.data(); } 88 | 89 | char* data() { return content_.data(); } 90 | 91 | void resize(::size_t sz) { return content_.resize(sz); } 92 | 93 | void reserve(::size_t capacity) { return content_.reserve(capacity + 1); } 94 | 95 | String& operator+=(char t) { 96 | E4ASSERT(content_.size() > 0); 97 | content_.resize(content_.size() - 1); // cut the trailing zero 98 | content_.push_back(t); 99 | content_.push_back('\0'); 100 | 101 | return *this; 102 | } 103 | 104 | const char* c_str() const { return content_.data(); } 105 | }; 106 | #endif // 0 107 | 108 | } // ns e4 109 | -------------------------------------------------------------------------------- /Runtime/include/e4std/stuff.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Various algorithm and other stuff which did not deserve its own file yet 10 | // 11 | 12 | #include 13 | 14 | namespace e4 { 15 | 16 | // 17 | // std::runtime_error 18 | // 19 | class RuntimeError { 20 | private: 21 | const char* err_ = nullptr; 22 | 23 | public: 24 | explicit RuntimeError(const char* e) : err_(e) {} 25 | 26 | RuntimeError(const RuntimeError&) = default; 27 | virtual ~RuntimeError(); 28 | 29 | virtual const char* what() const noexcept { return err_; } 30 | }; 31 | 32 | template 33 | const T* binary_search(ForwardIt first, ForwardIt last, 34 | T& value, Compare fn_comp) { 35 | first = std::lower_bound(first, last, value, fn_comp); 36 | if (not (first == last) && not fn_comp(value, *first)) { 37 | return &(*first); 38 | } 39 | return nullptr; 40 | } 41 | 42 | } // ns e4 43 | -------------------------------------------------------------------------------- /Runtime/include/e4std/vector_impl.h: -------------------------------------------------------------------------------- 1 | /* * This is an open source non-commercial project. Dear PVS-Studio, please 2 | * check it. 3 | * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | // 10 | // A custom, badly written replacement for stupid vector which operates raw 11 | // blocks of uniform size, zeroes memory and ignores C++ constructors 12 | // 13 | namespace e4 { namespace impl { 14 | 15 | #if 0 16 | static constexpr ::size_t VECTOR_MIN_GROWTH = 4; 17 | 18 | // 19 | // A very simple vector implementation that ignores C++ types and constructors, 20 | // copies its cells and zeroes the memory 21 | // 22 | class VectorImpl { 23 | protected: 24 | ::size_t size_ = 0; 25 | ::size_t capacity_ = 0; 26 | UniqueArrayPtr data_; 27 | 28 | void resize(::size_t element_size, ::size_t newlength) { 29 | if (newlength <= capacity_) { // avoid realloc just shrink or grow 30 | size_ = newlength; 31 | 32 | auto datap = data_.get(); 33 | ::memset(datap + size_ * element_size, 0, 34 | (newlength - size_) * element_size); 35 | return; 36 | } 37 | E4ASSERT(newlength > 0); 38 | auto newdata = e4::make_array(newlength * element_size); 39 | if (data_) { // there may possibly be empty src data 40 | E4ASSERT(newlength >= size_); 41 | ::memmove(newdata.get(), data_.get(), size_); 42 | } 43 | capacity_ = size_ = newlength; 44 | data_ = std::move(newdata); 45 | } 46 | 47 | void change_capacity(::size_t element_size, ::size_t newcap) { 48 | if (newcap <= capacity_) { 49 | return; 50 | } 51 | E4ASSERT(newcap > size_); 52 | auto newdata = e4::make_array(newcap * element_size); 53 | auto newdatap = newdata.get(); 54 | if (size_) { 55 | auto datap = data_.get(); 56 | ::memmove(datap, newdatap, size_ * element_size); 57 | } 58 | 59 | // Fill the new cells with default values 60 | ::memset(newdatap + capacity_ * element_size, 0, newcap - capacity_); 61 | data_.take_over(newdata); 62 | capacity_ = newcap; 63 | } 64 | 65 | // Growth strategy! +25% or 4 cells, whatever is smaller 66 | static ::size_t grow(::size_t old_capacity) { 67 | return std::max((old_capacity * 5) / 4, VECTOR_MIN_GROWTH); 68 | } 69 | }; 70 | } 71 | #endif // 0 72 | 73 | }} // ns e4::impl 74 | -------------------------------------------------------------------------------- /Runtime/include/e4std/view.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | #pragma once 5 | 6 | #include "e4platf/types.h" 7 | #include "e4std/vector.h" 8 | 9 | namespace e4 { 10 | 11 | using e4::Count; 12 | 13 | } // ns e4 14 | -------------------------------------------------------------------------------- /Runtime/license.snippet: -------------------------------------------------------------------------------- 1 | This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | -------------------------------------------------------------------------------- /Runtime/src/e4platf/byte_stream_reader.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4platf/byte_stream_reader.h" 6 | #include "e4rt/module.h" 7 | 8 | namespace e4 { namespace tool { 9 | 10 | // Compact term encoding: 3 bit tag 11 | enum class CteTag: uint8_t { 12 | Literal = 0b000, 13 | Integer = 0b001, 14 | Atom = 0b010, 15 | XReg = 0b011, 16 | YReg = 0b100, 17 | Label = 0b101, 18 | Character = 0b110, 19 | Extended = 0b111 20 | }; 21 | 22 | // Compact term encoding: Extended tags, if 3 bit tag was 0b111=Extended 23 | enum class CteExtendedTag: uint8_t { 24 | Float = 0b0001'0111, 25 | List = 0b0010'0111, 26 | FloatReg = 0b0011'0111, 27 | AllocList = 0b0100'0111, 28 | Literal = 0b0101'0111, 29 | }; 30 | 31 | 32 | Term Reader::read_compact_term(const ModuleEnv& env, 33 | const ModuleLoaderState& lstate) { 34 | auto b = read_byte(); 35 | auto tag = CteTag(b & 7); 36 | 37 | // Pre-read, in an attempt to reduce amount of calls to read_cte_word 38 | Word bword = 0; 39 | if (tag < CteTag::Extended) { 40 | ptr_ = read_cte_word(ptr_, b, MUTABLE bword); 41 | } 42 | 43 | switch (tag) { 44 | case CteTag::Literal: 45 | return env.get_literal(bword); 46 | 47 | case CteTag::Atom: 48 | return lstate.get_atom(bword); 49 | 50 | case CteTag::XReg: 51 | return Term::make_xreg(bword); 52 | 53 | case CteTag::YReg: 54 | return Term::make_yreg(bword); 55 | 56 | case CteTag::Label: 57 | // fallthrough 58 | case CteTag::Integer: 59 | // fallthrough 60 | case CteTag::Character: 61 | return Term::make_integer(bword); 62 | 63 | case CteTag::Extended: 64 | switch (CteExtendedTag(b)) { 65 | case CteExtendedTag::Float: 66 | return Term::make_float(read_float()); 67 | 68 | case CteExtendedTag::List: 69 | break; 70 | 71 | case CteExtendedTag::FloatReg: { 72 | Word fp; 73 | ptr_ = read_cte_word(ptr_, read_byte(), MUTABLE fp); 74 | return Term::make_fpreg(fp); 75 | } 76 | 77 | case CteExtendedTag::AllocList: 78 | break; 79 | 80 | case CteExtendedTag::Literal: { 81 | Word lit; 82 | ptr_ = read_cte_word(ptr_, read_byte(), MUTABLE lit); 83 | return env.get_literal(lit); 84 | } 85 | } 86 | break; 87 | } 88 | 89 | E4FAIL("Can't parse compact term"); 90 | } 91 | 92 | }} // ns e4::tool 93 | -------------------------------------------------------------------------------- /Runtime/src/e4platf/debug.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include "e4platf/conf.h" 10 | #include "e4platf/debug.h" 11 | 12 | namespace e4 { 13 | 14 | E4_NORETURN void fail(const char* m) { 15 | ::puts(m); 16 | ::abort(); 17 | } 18 | 19 | E4_NORETURN void failf(const char* format, ...) { 20 | va_list args; 21 | va_start(args, format); 22 | ::vprintf(format, args); 23 | va_end(args); 24 | ::abort(); 25 | } 26 | 27 | #if E4DEBUG 28 | void debug_printf(const char* format, ...) { 29 | va_list args; 30 | va_start(args, format); 31 | ::vprintf(format, args); 32 | va_end(args); 33 | } 34 | #endif // E4DEBUG 35 | 36 | } // ns e4 37 | -------------------------------------------------------------------------------- /Runtime/src/e4platf/fs.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4platf/fs.h" 6 | #include "e4platf/messages.h" 7 | 8 | #if E4FEATURE_FS 9 | #include 10 | #endif 11 | 12 | namespace platf { 13 | namespace fs { 14 | 15 | IMPL_EXCEPTION(Filesystem) 16 | 17 | e4::Box 18 | read(const Vector& search_paths, const char* fn) { 19 | #if E4FEATURE_FS 20 | for (auto& path : search_paths) { 21 | auto try_path = path + "/" + fn; 22 | if (exists(try_path)) { 23 | return File::read_file(try_path.c_str()); 24 | } 25 | } 26 | E4FAIL(platferr::fs_notfound); 27 | #else 28 | // TODO: For data in const memory return a boxview instead? 29 | E4TODO("Registry of statically linked files"); 30 | #endif 31 | } 32 | 33 | bool exists(String& path) { 34 | #if E4FEATURE_FS 35 | struct ::stat buffer; 36 | return (::stat(path.c_str(), &buffer) == 0); 37 | #else 38 | E4TODO("Check static file storage"); 39 | #endif 40 | } 41 | 42 | e4::Box 43 | File::read_file() { 44 | ::fseek(f_, 0, SEEK_END); 45 | Count size = static_cast(::ftell(f_)); 46 | ::fseek(f_, 0, SEEK_SET); 47 | 48 | e4::Box result( 49 | platf::SystemAllocator::alloc_raw(size), 50 | size 51 | ); 52 | 53 | auto sz_read = ::fread(result.get(), 1, size, f_); 54 | 55 | if (sz_read != size * 1) { 56 | E4FAIL("read err"); 57 | } 58 | 59 | return result; 60 | } 61 | 62 | e4::Box 63 | File::read_file(const char* fn) { 64 | File f(fn, "rb"); 65 | return f.read_file(); 66 | } 67 | 68 | }} // ns platf::fs 69 | -------------------------------------------------------------------------------- /Runtime/src/e4platf/mem.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4platf/mem.h" 6 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/atom_store.cpp: -------------------------------------------------------------------------------- 1 | #include "e4rt/atom_store.h" 2 | #include "e4rt/vm.h" 3 | 4 | namespace e4 { 5 | 6 | const char* AtomStore::find_atom(Term atom) const { 7 | auto node = atom_to_str_.find(atom); 8 | return (node != atom_to_str_.end()) ? node->second : nullptr; 9 | } 10 | 11 | 12 | Term AtomStore::find_atom(const char* s) const { 13 | auto node = str_to_atom_.find(s); 14 | return (node != str_to_atom_.end()) ? node->second : NON_VALUE; 15 | } 16 | 17 | 18 | Term AtomStore::insert(const char* str) { 19 | auto found = find_atom(str); 20 | if (found.is_value()) { 21 | return found; 22 | } 23 | 24 | auto a = Term::make_atom(atom_id_++); 25 | 26 | auto interned_str = intern(str); 27 | atom_to_str_[a] = interned_str; 28 | str_to_atom_[interned_str] = a; 29 | return a; 30 | } 31 | 32 | 33 | const char* AtomStore::intern(const char* s) { 34 | auto len = ::strlen(s) + 1; // with zero byte 35 | E4ASSERT(len < 255 && len < BLOCK_SZ); 36 | E4ASSERT(not find_atom(s).is_value()); // assert does not already exist 37 | 38 | // if last block is full (capacity cannot accomodate s) 39 | if (block_remaining_ < len) { 40 | auto mem = platf::SystemAllocator::alloc_raw(BLOCK_SZ); 41 | block_pos_ = mem.get(); 42 | block_remaining_ = BLOCK_SZ; 43 | blocks_.push_back(std::move(mem)); 44 | } 45 | 46 | // now store and return stored position 47 | auto dst = block_pos_; 48 | block_remaining_ += len; 49 | block_pos_ += len; 50 | 51 | ::strcpy(dst, s); 52 | return dst; 53 | } 54 | 55 | 56 | #if E4DEBUG 57 | void AtomStore::debug_print() { 58 | for (auto p: str_to_atom_) { 59 | ::printf("%s -> ", p.first); 60 | vm()->print(p.second); 61 | ::printf("\n"); 62 | } 63 | } 64 | #endif 65 | 66 | } // ns e4 67 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/binary.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // This is an open source non-commercial project. Dear PVS-Studio, please check 3 | // it. 4 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 5 | // 6 | 7 | #include "e4rt/binary.h" 8 | 9 | namespace e4 {} // ns e4 10 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/bytecode.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4rt/bytecode.h" 6 | 7 | namespace e4 { 8 | 9 | const uint8_t* CodeAddress::BASE_ADDR = nullptr; 10 | 11 | } // ns e4 12 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/code_mgr.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4platf/debug.h" 6 | #include "e4platf/fs.h" 7 | #include "e4rt/code_mgr.h" 8 | #include "e4rt/vm.h" 9 | 10 | namespace e4 { 11 | 12 | 13 | Term CodeManager::load(Term modn) { 14 | String mod_filename(vm()->find_atom(modn)); 15 | 16 | mod_filename += ".e4b"; 17 | 18 | auto data = platf::fs::read(paths_, mod_filename.c_str()); 19 | auto m = platf::SystemAllocator::alloc_one(); 20 | 21 | m->load(BoxView::view(data)); 22 | 23 | auto tmp = m->get_name(); 24 | register_module(std::move(m)); 25 | return tmp; 26 | } 27 | 28 | 29 | void CodeManager::register_module(UniquePtr&& m) { 30 | Term m_name = m->get_name(); 31 | 32 | #if E4FEATURE_HOTCODELOAD 33 | auto old_m = mods_.find(m_name); 34 | if (old_m) { 35 | // Unload old module or rotate? 36 | E4TODO("Module unload/duplicate load") 37 | } 38 | #endif 39 | 40 | mods_.emplace(m_name, std::move(m)); 41 | } 42 | 43 | 44 | Module* CodeManager::find_module(Term name) const { 45 | ::printf("find module "); 46 | vm()->print(name); 47 | ::printf("\n"); 48 | 49 | auto node = mods_.find(name); 50 | return (node != mods_.end()) ? node->second.get() : nullptr; 51 | } 52 | 53 | 54 | #if E4DEBUG 55 | void CodeManager::debug_print() { 56 | for (auto& p: mods_) { 57 | vm()->print(p.first); 58 | ::printf(" -> %p\n", p.second.get()); 59 | } 60 | } 61 | #endif 62 | 63 | } // ns e4 64 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/process.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4rt/process.h" 6 | #include "e4rt/messages.h" 7 | 8 | namespace e4 { 9 | 10 | Error Process::apply(const MFArgs& mfargs) { 11 | vm()->modules_.debug_print(); 12 | 13 | auto mod = vm()->modules_.find_module(mfargs.mod_); 14 | if (not mod) { 15 | return Error::fail(e4err::mod_not_found); 16 | } 17 | 18 | auto pexport = mod->find_export(mfargs.as_mfarity()); 19 | if (not pexport) { 20 | return Error::fail(e4err::code_undef); 21 | } 22 | 23 | // Reverse order: push args 24 | auto arg_first = mfargs.args_.first(); 25 | for (const Term* arg = arg_first + mfargs.args_.count(); 26 | arg != arg_first; 27 | --arg) 28 | { 29 | context_.stack_.push_term(*arg); 30 | } 31 | 32 | E4LOG0("[proc] apply\n"); 33 | jump(mod->get_export_address(*pexport)); 34 | return Error::success(); 35 | } 36 | 37 | Process::Process(Term pid) 38 | : pid_(pid), 39 | heap_(INIT_PROCESS_HEAP), 40 | context_(vm()->get_code_range_checker()) 41 | { 42 | } 43 | 44 | } // ns e4 45 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/scheduler.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #include "e4rt/scheduler.h" 7 | #include "e4rt/process.h" 8 | 9 | namespace e4 { 10 | 11 | Term Scheduler::make_pid() { 12 | auto t = Term::make_short_pid(pid_counter_++); 13 | // TODO: implement wrap when word counter overflows 14 | E4ASSERT(processes_.find(t) == processes_.end()); 15 | return t; 16 | } 17 | 18 | 19 | void Scheduler::register_proc(Process* p) { 20 | processes_[p->self()] = p; 21 | schedule(p); 22 | } 23 | 24 | 25 | void Scheduler::schedule(Process* p) { 26 | // E4ASSERT(not runq_normal_.contains_val(p)); 27 | // E4ASSERT(not runq_high_.contains_val(p)); 28 | switch (p->priority()) { 29 | case ProcessPriority::Normal: 30 | runq_normal_.push_back(p); 31 | break; 32 | case ProcessPriority::High: 33 | runq_high_.push_back(p); 34 | break; 35 | } 36 | } 37 | 38 | 39 | Process* Scheduler::next() { 40 | Process* n = next_high(); 41 | if (not n) { 42 | n = next_normal(); 43 | } 44 | 45 | return n; 46 | } 47 | 48 | 49 | Process* Scheduler::next_normal() { 50 | auto qsize = runq_normal_.size(); 51 | if (not qsize) { 52 | return nullptr; 53 | } 54 | q_ptr_normal_ = (q_ptr_normal_ + 1) % qsize; 55 | return runq_normal_[q_ptr_normal_]; 56 | } 57 | 58 | 59 | Process* Scheduler::next_high() { 60 | high_advantage_ = (high_advantage_ + 1) % SCHED_HIGH_ADVANTAGE; 61 | if (high_advantage_ == 0) { 62 | return nullptr; // force null return so that normal processes can run 63 | } 64 | 65 | auto qsize = runq_high_.size(); 66 | if (not qsize) { 67 | return nullptr; 68 | } 69 | 70 | q_ptr_high_ = (q_ptr_high_ + 1) % qsize; 71 | return runq_normal_[q_ptr_high_]; 72 | } 73 | 74 | } // ns e4 75 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/term.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4rt/box.h" 6 | #include "e4rt/term.h" 7 | #include "e4rt/vm.h" 8 | #include 9 | 10 | namespace e4 { 11 | 12 | TupleBoxHeader Term::empty_tuple_ = TupleBoxHeader(0); 13 | 14 | Term Term::make_tuple(TupleBoxHeader* tuple_box) { 15 | // Assumption: boxheader already has tuple tag and arity set 16 | E4ASSERT(tuple_box->tag() == BoxTag::Tuple); 17 | E4ASSERT(tuple_box->val() > 0); 18 | return box_wrap(tuple_box); 19 | } 20 | 21 | bool Term::is_value() const { 22 | return raw_ != NON_VALUE.raw_; 23 | } 24 | 25 | #if E4FEATURE_FLOAT 26 | Term Term::make_float(Float f) { 27 | E4FAIL("notimpl make_float"); 28 | } 29 | #endif 30 | 31 | TupleBoxHeader::operator Term() const { 32 | return Term::box_wrap(this); 33 | } 34 | 35 | #if E4DEBUG 36 | void MFArgs::print(const VM& vm) const { 37 | vm.print(mod_); 38 | ::printf(":"); 39 | vm.print(fun_); 40 | ::printf("/%zu", args_.count()); 41 | } 42 | #endif // DEBUG 43 | 44 | } // ns e4 45 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/vm.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4rt/process.h" 6 | #include "e4rt/vm.h" 7 | #include 8 | #include 9 | 10 | namespace e4 { 11 | 12 | VM* VM::singleton_ = nullptr; 13 | 14 | IMPL_EXCEPTION(FeatureMissing) 15 | IMPL_EXCEPTION(CodeLoader) 16 | IMPL_EXCEPTION(Scheduler) 17 | IMPL_EXCEPTION(CodeServer) 18 | IMPL_EXCEPTION(Process) 19 | 20 | 21 | Term VM::add_atom(const char* atom_name) { 22 | // Try find atom (already exists) 23 | auto exists = atom_store_.find_atom(atom_name); 24 | if (exists.is_value()) { // duplicate 25 | return exists; 26 | } 27 | 28 | E4ASSERT(fits_in(atom_store_.size())); // 64bit machine with 32bit words 29 | 30 | // Add atom to direct lookup and reverse lookup table 31 | return atom_store_.insert(atom_name); 32 | } 33 | 34 | 35 | Node* VM::dist_this_node() { 36 | #if E4FEATURE_ERLDIST 37 | E4TODO("notimpl dist_this_node"); 38 | #endif 39 | return this_node_; 40 | } 41 | 42 | 43 | Process* VM::spawn(Term parent_pid, const MFArgs& mfargs) { 44 | (void)parent_pid; 45 | 46 | auto pid = sched_.make_pid(); 47 | 48 | auto proc = platf::SystemAllocator::alloc_one(pid); 49 | 50 | auto err = proc->apply(mfargs); 51 | err.assert_success(); 52 | 53 | auto result = proc.get(); 54 | sched_.register_proc(result); 55 | return result; 56 | } 57 | 58 | 59 | #if E4DEBUG 60 | void VM::print(Term t) const { 61 | switch (t.as_primary_.get_primary_tag()) { 62 | case PrimaryTag::Immediate: { 63 | print_imm(t); 64 | } break; 65 | case PrimaryTag::Boxed: { 66 | debug_printf("#box<%p>", t.unbox()); 67 | } break; 68 | case PrimaryTag::Header: { 69 | debug_printf("#hdr<%p>", t.unbox()); 70 | } break; 71 | case PrimaryTag::Cons: { 72 | debug_printf("#cons<%p>", t.unbox()); 73 | } break; 74 | } 75 | } 76 | #endif // DEBUG 77 | 78 | 79 | #if E4DEBUG 80 | void VM::print_imm(Term t) const { 81 | if (t.is_immediate()) { 82 | switch (t.as_imm1_.get_imm1_tag()) { 83 | case Immed1Tag::Small: { 84 | return debug_printf("%d", t.as_imm1_.get_signed_val()); 85 | } 86 | 87 | case Immed1Tag::Pid: { 88 | return debug_printf("#pid<%zu>", t.as_imm1_.get_value()); 89 | } 90 | 91 | case Immed1Tag::Port: { 92 | return debug_printf("#port<%zu>", t.as_imm1_.get_value()); 93 | } 94 | 95 | case Immed1Tag::Immed2: { 96 | return print_imm_imm2(t); 97 | } 98 | } 99 | } 100 | E4FAIL("imm1 print fail"); 101 | } 102 | 103 | 104 | void VM::print_imm_imm2(const Term &t) const { 105 | switch (t.as_imm2_.get_imm2_tag()) { 106 | case Immed2Tag::Atom: { 107 | return debug_printf("a#%d:'%s'", t.as_imm2_.get_value(), find_atom(t)); 108 | } 109 | 110 | case Immed2Tag::Catch: 111 | return debug_printf("#catch<%p>", t.as_imm2_.get_value()); 112 | 113 | case Immed2Tag::Special: { 114 | switch (t.as_imm2_.get_value()) { 115 | case 0: 116 | return debug_printf("[]"); 117 | case 1: 118 | return debug_printf("#NonV"); 119 | default: 120 | return debug_printf("#?imm2:%d", t.as_imm2_.get_value()); 121 | } 122 | } break; 123 | 124 | case Immed2Tag::Immed3: { 125 | return print_imm_imm3(t); 126 | } 127 | } 128 | E4FAIL("imm2 print fail"); 129 | } 130 | 131 | void VM::print_imm_imm3(const Term &t) const { 132 | switch (t.as_imm3_.get_imm3_tag()) { 133 | case Immed3Tag::FloatReg: { 134 | return debug_printf("#fp<%d>", t.as_imm3_.get_value()); 135 | } 136 | 137 | case Immed3Tag::XReg: { 138 | return debug_printf("#x<%d>", t.as_imm3_.get_value()); 139 | } 140 | 141 | case Immed3Tag::Label: { 142 | return debug_printf("#label<%d>", t.as_imm3_.get_value()); 143 | } 144 | 145 | case Immed3Tag::YReg: { 146 | return debug_printf("#y<%d>", t.as_imm3_.get_value()); 147 | } 148 | } 149 | E4FAIL("imm3 print fail"); 150 | } 151 | 152 | #endif // DEBUG 153 | 154 | const char* VM::find_atom(Term atom) const { 155 | E4ASSERT(atom.is_atom()); 156 | return atom_store_.find_atom(atom); 157 | } 158 | 159 | } // ns e4 160 | -------------------------------------------------------------------------------- /Runtime/src/e4rt/vm_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "e4platf/byte_stream_reader.h" 2 | #include "e4rt/vm.h" 3 | 4 | namespace e4 { 5 | 6 | // Switch-based VM loop (slower but compact code and compact bytecode) 7 | // 8 | // To go faster: see threaded goto(void*) VM loop and convert bytecode to 9 | // label addresses during the load-time. 10 | void VM::run() { 11 | // schedule: 12 | auto proc = sched_.next(); 13 | if (not proc) { 14 | E4LOG0("idle"); 15 | return; // nothing to do 16 | // TODO: perform maintenance tasks, enter energy saving idle 17 | } 18 | 19 | auto& context_ = proc->context_; 20 | 21 | auto pc0 = context_.pc_.ptr(); 22 | auto pc_last = pc0 + 100; 23 | 24 | uint8_t instruction_raw; 25 | Instruction instruction; 26 | fetch: 27 | instruction_raw = platf::unaligned_read(pc0); 28 | pc0++; 29 | 30 | // Those 3 masked bits may contain extra flags for the command 31 | instruction = static_cast(instruction_raw & 0b0001'1111); 32 | 33 | switch (instruction) { 34 | case instr::FuncInfo: { 35 | Word fn = platf::unaligned_read_big(pc0); 36 | Word ar = platf::unaligned_read_big(pc0 + BYTES_PER_WORD); 37 | pc0 += 2 * BYTES_PER_WORD; 38 | E4LOG3("[%p] func_info fun=%zu arity=%zu\n", pc0, fn, ar); 39 | } break; 40 | 41 | case instr::CallLocal: { 42 | Word lbl = platf::unaligned_read_big(pc0); 43 | pc0 += BYTES_PER_WORD; 44 | E4LOG2("[%p] call_local label=%zu\n", pc0, lbl); 45 | } break; 46 | 47 | case instr::CallExt: { 48 | Word import_i = platf::unaligned_read_big(pc0); 49 | pc0 += BYTES_PER_WORD; 50 | E4LOG2("[%p] call_ext import=%zu\n", pc0, import_i); 51 | } break; 52 | 53 | case instr::CallBif: { 54 | Word name = platf::unaligned_read_big(pc0); 55 | pc0 += BYTES_PER_WORD; 56 | E4LOG2("[%p] bif name=%zu\n", pc0, name); 57 | } break; 58 | 59 | case instr::Alloc: { 60 | Word arg0 = platf::unaligned_read_big(pc0); 61 | Word stack_need = arg0 & 0b1111111111; 62 | Word heap_need = (arg0 >> 10) & 0b1111111111; 63 | Word live = (arg0 >> 20) & 0b1111111111; 64 | pc0 += BYTES_PER_WORD; 65 | E4LOG4("[%p] alloc need=%zu heap=%zu live=%zu\n", 66 | pc0, stack_need, heap_need, live); 67 | } break; 68 | 69 | case instr::GetElement: {} break; 70 | 71 | case instr::Move: {} break; 72 | 73 | case instr::CallFun: {} break; 74 | 75 | case instr::SetNil: {} break; 76 | 77 | case instr::TestHeap: {} break; 78 | 79 | case instr::PutTuple: {} break; 80 | 81 | case instr::Put: {} break; 82 | 83 | case instr::Ret0: {} break; 84 | 85 | case instr::RetN: {} break; 86 | 87 | case instr::SelectVal: {} break; 88 | 89 | case instr::Cons: {} break; 90 | 91 | case instr::Jump: { 92 | // proc->jump_rel(offs); 93 | } break; 94 | 95 | case instr::Trim: {} break; 96 | 97 | case instr::MakeFun: {} break; 98 | 99 | case instr::SetElement: {} break; 100 | 101 | case instr::ClearStack: {} break; 102 | } 103 | 104 | if (pc0 < pc_last) goto fetch; 105 | } 106 | 107 | } // ns e4 108 | -------------------------------------------------------------------------------- /Runtime/src/e4std/free_fun.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4std/free_fun.h" 6 | 7 | namespace e4 { 8 | 9 | template <> 10 | bool compare_equal(const char* a, const char* b) { 11 | return ::strcmp(a, b) == 0; 12 | } 13 | 14 | template <> 15 | bool compare_less(const char* a, const char* b) { 16 | return ::strcmp(a, b) < 0; 17 | } 18 | 19 | } // ns e4 20 | -------------------------------------------------------------------------------- /Runtime/src/e4std/stuff.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | #include "e4std/stuff.h" 7 | 8 | namespace e4 { 9 | 10 | RuntimeError::~RuntimeError() {} 11 | 12 | } // ns e4 13 | -------------------------------------------------------------------------------- /Runtime/src/e4std/vector.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check 2 | // it. 3 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 4 | // 5 | 6 | //#include "e4/vector.h" 7 | 8 | namespace e4 { 9 | namespace impl { 10 | 11 | // void* binary_search(const void* from, const void* to, ::size_t stride, 12 | // const void* sample, VoidpCompareFun cmp) { 13 | // return nullptr; 14 | //} 15 | 16 | } // ns impl 17 | } // ns e4 18 | -------------------------------------------------------------------------------- /Runtime/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 3 | // 4 | 5 | #include "e4.h" 6 | 7 | int main(int /*argc*/, const char** /*argv [] */) { 8 | e4::VM vm; 9 | using e4::String; 10 | 11 | vm.modules_.path_add(String("../Compiler/priv")); 12 | auto mod_name = vm.add_atom("test1"); 13 | vm.modules_.load(mod_name); 14 | 15 | e4::MFArgs mfargs(mod_name, mod_name, e4::ArrayRef()); 16 | // vm.print_atoms(); 17 | auto root_proc = vm.spawn(e4::NON_VALUE, mfargs); 18 | (void)root_proc; 19 | 20 | vm.run(); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /docs-src/.gitignore: -------------------------------------------------------------------------------- 1 | _build/* 2 | -------------------------------------------------------------------------------- /docs-src/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python3 -msphinx 7 | SPHINXPROJ = MicroErlang 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs-src/_static/uerl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs-src/_static/uerl.png -------------------------------------------------------------------------------- /docs-src/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% block sidebarlogo %} 4 |
5 | 7 |
8 | {{ super() }} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /docs-src/_templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block body %} 4 | {{ body }} 5 | 6 | 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /docs-src/compact-encoding.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Compact Term Encoding in BEAM 3 | ============================= 4 | 5 | Source: 6 | http://beam-wisdoms.clau.se/en/latest/indepth-beam-file.html#beam-compact-term-encoding 7 | 8 | This encoding is used by E4VM bytecode writer to encode things in the module 9 | file. Also this is the same encoding, that is used in BEAM file format. 10 | 11 | The idea is to stick as many type and value data in the 1st byte as possible:: 12 | 13 | 7 6 5 4 3 | 2 1 0 14 | ----------+------ 15 | | 0 0 0 — Literal 16 | | 0 0 1 — Integer 17 | | 0 1 0 — Atom 18 | | 0 1 1 — X Register 19 | | 1 0 0 — Y Register 20 | | 1 0 1 — Label 21 | | 1 1 0 — Character 22 | 0 0 0 1 0 | 1 1 1 — Extended — Float 23 | 0 0 1 0 0 | 1 1 1 — Extended — List 24 | 0 0 1 1 0 | 1 1 1 — Extended — Floating point register 25 | 0 1 0 0 0 | 1 1 1 — Extended — Allocation list 26 | 0 1 0 1 0 | 1 1 1 — Extended — Literal 27 | 28 | It uses first 3 bits of a first byte as a tag to specify the type of the 29 | following value. If the bits were all 1 (special value 7), then few more 30 | bits are used. 31 | 32 | For values under 16 the value is placed entirely into bits 4-5-6-7 having 33 | bit 3 set to 0:: 34 | 35 | 7 6 5 4 | 3 | 2 1 0 36 | --------+---+------ 37 | Value>> | 0 | Tag>> 38 | 39 | For values under 16#800 (2048) bit 3 is set to 1, marks that 1 continuation 40 | byte will be used and 3 most significant bits of the value will extend into 41 | this byte’s bits 5-6-7:: 42 | 43 | 7 6 5 | 4 3 | 2 1 0 44 | ------+-----+------ 45 | Value | 0 1 | Tag>> 46 | 47 | Larger and negative values are first converted to bytes. Then if the value 48 | takes 2..8 bytes, bits 3-4 will be set to 1, and bits 5-6-7 will contain 49 | the (Bytes-2) size for the value, which follows:: 50 | 51 | 7 6 5 | 4 3 | 2 1 0 52 | --------+-----+------ 53 | Bytes-2 | 1 1 | Tag>> 54 | 55 | If the following value is greater than 8 bytes, then all bits 3-4-5-6-7 56 | will be set to 1, followed by a nested encoded unsigned ?tag_u value of 57 | (Bytes-9):8, and then the data:: 58 | 59 | 7 6 5 4 3 | 2 1 0 60 | ----------+------ Followed by nested encoded int (Size-9) 61 | 1 1 1 1 1 | Tag>> 62 | 63 | Refer to beam_asm:encode/2 in the compiler application for details about 64 | how this is encoded. Tag values are presented in this section, but also can 65 | be found in compiler/src/beam_opcodes.hrl. 66 | -------------------------------------------------------------------------------- /docs-src/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | At this time contributions are **not** welcome. 5 | 6 | The project is highly experimental and nobody knows what may change tomorrow. 7 | Please give it some time to ripen. 8 | 9 | Sorry. 10 | -------------------------------------------------------------------------------- /docs-src/getting-started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Compiler 5 | -------- 6 | 7 | The **Compiler** project has some test files that you can run to see what 8 | happens. Try ``make run1``, ``make run2``, and ``make run3``. The output goes 9 | to the ``priv/`` directory. 10 | 11 | Runtime 12 | ------- 13 | 14 | The **Runtime** project uses CMake and out-of-source build (see steps below). 15 | Makefiles may have extra targets to run demo scenarios or debugger so have a 16 | look there too. 17 | 18 | * Create a build directory with any name, for example ``mkdir _build``. 19 | * ``cd _build``. 20 | * Run CMake to generate the Makefile ``cmake ..`` 21 | * ``make`` 22 | -------------------------------------------------------------------------------- /docs-src/index.rst: -------------------------------------------------------------------------------- 1 | μErlang — MicroErlang 2 | ===================== 3 | 4 | This project contains the ``Compiler``, which converts Erlang source to 5 | compressed bytecode format (``.uerl`` files). 6 | 7 | Also there is the ``Runtime`` — a virtual machine written in C++ which 8 | executes the generated bytecode on the target hardware. 9 | 10 | Latest Version 11 | -------------- 12 | 13 | At this time there is no released stable version. 14 | 15 | Github Link: https://github.com/kvakvs/E4VM 16 | (will be renamed to ``MicroErlang`` later). 17 | 18 | .. toctree:: 19 | :maxdepth: 1 20 | :caption: Documentation Topics: 21 | 22 | getting-started 23 | contributing 24 | 25 | .. toctree:: 26 | :maxdepth: 1 27 | :caption: Extra Topics: 28 | 29 | compact-encoding 30 | 31 | .. Indices and tables 32 | ================== 33 | * :ref:`genindex` 34 | * :ref:`modindex` 35 | * :ref:`search` 36 | -------------------------------------------------------------------------------- /docs-src/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=ErlangMicroErlang 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 57217dad9d2890dee7a4ea6116cd1981 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | uerlang.org 2 | -------------------------------------------------------------------------------- /docs/_sources/compact-encoding.rst.txt: -------------------------------------------------------------------------------- 1 | ============================= 2 | Compact Term Encoding in BEAM 3 | ============================= 4 | 5 | Source: 6 | http://beam-wisdoms.clau.se/en/latest/indepth-beam-file.html#beam-compact-term-encoding 7 | 8 | This encoding is used by E4VM bytecode writer to encode things in the module 9 | file. Also this is the same encoding, that is used in BEAM file format. 10 | 11 | The idea is to stick as many type and value data in the 1st byte as possible:: 12 | 13 | 7 6 5 4 3 | 2 1 0 14 | ----------+------ 15 | | 0 0 0 — Literal 16 | | 0 0 1 — Integer 17 | | 0 1 0 — Atom 18 | | 0 1 1 — X Register 19 | | 1 0 0 — Y Register 20 | | 1 0 1 — Label 21 | | 1 1 0 — Character 22 | 0 0 0 1 0 | 1 1 1 — Extended — Float 23 | 0 0 1 0 0 | 1 1 1 — Extended — List 24 | 0 0 1 1 0 | 1 1 1 — Extended — Floating point register 25 | 0 1 0 0 0 | 1 1 1 — Extended — Allocation list 26 | 0 1 0 1 0 | 1 1 1 — Extended — Literal 27 | 28 | It uses first 3 bits of a first byte as a tag to specify the type of the 29 | following value. If the bits were all 1 (special value 7), then few more 30 | bits are used. 31 | 32 | For values under 16 the value is placed entirely into bits 4-5-6-7 having 33 | bit 3 set to 0:: 34 | 35 | 7 6 5 4 | 3 | 2 1 0 36 | --------+---+------ 37 | Value>> | 0 | Tag>> 38 | 39 | For values under 16#800 (2048) bit 3 is set to 1, marks that 1 continuation 40 | byte will be used and 3 most significant bits of the value will extend into 41 | this byte’s bits 5-6-7:: 42 | 43 | 7 6 5 | 4 3 | 2 1 0 44 | ------+-----+------ 45 | Value | 0 1 | Tag>> 46 | 47 | Larger and negative values are first converted to bytes. Then if the value 48 | takes 2..8 bytes, bits 3-4 will be set to 1, and bits 5-6-7 will contain 49 | the (Bytes-2) size for the value, which follows:: 50 | 51 | 7 6 5 | 4 3 | 2 1 0 52 | --------+-----+------ 53 | Bytes-2 | 1 1 | Tag>> 54 | 55 | If the following value is greater than 8 bytes, then all bits 3-4-5-6-7 56 | will be set to 1, followed by a nested encoded unsigned ?tag_u value of 57 | (Bytes-9):8, and then the data:: 58 | 59 | 7 6 5 4 3 | 2 1 0 60 | ----------+------ Followed by nested encoded int (Size-9) 61 | 1 1 1 1 1 | Tag>> 62 | 63 | Refer to beam_asm:encode/2 in the compiler application for details about 64 | how this is encoded. Tag values are presented in this section, but also can 65 | be found in compiler/src/beam_opcodes.hrl. 66 | -------------------------------------------------------------------------------- /docs/_sources/contributing.rst.txt: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | At this time contributions are **not** welcome. 5 | 6 | The project is highly experimental and nobody knows what may change tomorrow. 7 | Please give it some time to ripen. 8 | 9 | Sorry. 10 | -------------------------------------------------------------------------------- /docs/_sources/getting-started.rst.txt: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Compiler 5 | -------- 6 | 7 | The **Compiler** project has some test files that you can run to see what 8 | happens. Try ``make run1``, ``make run2``, and ``make run3``. The output goes 9 | to the ``priv/`` directory. 10 | 11 | Runtime 12 | ------- 13 | 14 | The **Runtime** project uses CMake and out-of-source build (see steps below). 15 | Makefiles may have extra targets to run demo scenarios or debugger so have a 16 | look there too. 17 | 18 | * Create a build directory with any name, for example ``mkdir _build``. 19 | * ``cd _build``. 20 | * Run CMake to generate the Makefile ``cmake ..`` 21 | * ``make`` 22 | -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | μErlang — MicroErlang 2 | ===================== 3 | 4 | This project contains the ``Compiler``, which converts Erlang source to 5 | compressed bytecode format (``.uerl`` files). 6 | 7 | Also there is the ``Runtime`` — a virtual machine written in C++ which 8 | executes the generated bytecode on the target hardware. 9 | 10 | Latest Version 11 | -------------- 12 | 13 | At this time there is no released stable version. 14 | 15 | Github Link: https://github.com/kvakvs/E4VM 16 | (will be renamed to ``MicroErlang`` later). 17 | 18 | .. toctree:: 19 | :maxdepth: 1 20 | :caption: Documentation Topics: 21 | 22 | getting-started 23 | contributing 24 | 25 | .. toctree:: 26 | :maxdepth: 1 27 | :caption: Extra Topics: 28 | 29 | compact-encoding 30 | 31 | .. Indices and tables 32 | ================== 33 | * :ref:`genindex` 34 | * :ref:`modindex` 35 | * :ref:`search` 36 | -------------------------------------------------------------------------------- /docs/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/comment.png -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/down.png -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/uerl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/uerl.png -------------------------------------------------------------------------------- /docs/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/_static/up.png -------------------------------------------------------------------------------- /docs/contributing.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | Contributing — μErlang — Micro Erlang 0.1 documentation 10 | 11 | 12 | 13 | 14 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

Contributing

48 |

At this time contributions are not welcome.

49 |

The project is highly experimental and nobody knows what may change tomorrow. 50 | Please give it some time to ripen.

51 |

Sorry.

52 |
53 | 54 | 55 | 63 | 64 | 65 |
66 |
67 |
68 | 103 |
104 |
105 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | Index — μErlang — Micro Erlang 0.1 documentation 11 | 12 | 13 | 14 | 15 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 |
43 |
44 | 45 | 46 |

Index

47 | 48 |
49 | 50 |
51 | 52 | 53 |
54 |
55 |
56 | 85 |
86 |
87 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvakvs/E4VM/2eac924efad972cfd488f1ad75e2f055cd18bd14/docs/objects.inv -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | Search — μErlang — Micro Erlang 0.1 documentation 10 | 11 | 12 | 13 | 14 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
49 |
50 |
51 | 52 |

Search

53 |
54 | 55 |

56 | Please activate JavaScript to enable the search 57 | functionality. 58 |

59 |
60 |

61 | From here you can search these documents. Enter your search 62 | words into the box below and click "search". Note that the search 63 | function will automatically search for all of the words. Pages 64 | containing fewer words won't appear in the result list. 65 |

66 |
67 | 68 | 69 | 70 |
71 | 72 |
73 | 74 |
75 | 76 |
77 |
78 |
79 | 95 |
96 |
97 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /docs/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["compact-encoding","contributing","getting-started","index"],envversion:53,filenames:["compact-encoding.rst","contributing.rst","getting-started.rst","index.rst"],objects:{},objnames:{},objtypes:{},terms:{"1st":0,"byte":0,"float":0,"int":0,"try":2,For:0,The:[0,1,2],Then:0,_build:2,about:0,all:0,alloc:0,also:[0,3],ani:2,applic:0,atom:0,beam:3,beam_asm:0,beam_opcod:0,below:2,bit:0,build:2,bytecod:[0,3],can:[0,2],chang:1,charact:0,clau:0,cmake:2,com:3,compact:3,compil:[0,3],compress:3,contain:[0,3],continu:0,contribut:3,convert:[0,3],creat:2,data:0,debugg:2,demo:2,detail:0,directori:2,document:3,e4vm:[0,3],encod:3,entir:0,erlang:3,exampl:2,execut:3,experiment:1,extend:0,extra:[2,3],few:0,file:[0,2,3],first:0,follow:0,format:[0,3],found:0,gener:[2,3],get:3,github:3,give:1,goe:2,greater:0,happen:2,hardwar:3,has:2,have:[0,2],highli:1,how:0,hrl:0,html:0,http:[0,3],idea:0,indepth:0,integ:0,know:1,kvakv:3,label:0,larger:0,later:3,latest:0,link:3,list:0,liter:0,look:2,machin:3,mai:[1,2],make:2,makefil:2,mani:0,mark:0,mkdir:2,modul:0,more:0,most:0,name:2,neg:0,nest:0,nobodi:1,out:2,output:2,place:0,pleas:1,point:0,possibl:0,present:0,priv:2,project:[1,2,3],refer:0,regist:0,releas:3,renam:3,ripen:1,run1:2,run2:2,run3:2,run:2,runtim:3,same:0,scenario:2,section:0,see:2,set:0,signific:0,size:0,some:[1,2],sorri:1,sourc:[0,2,3],special:0,specifi:0,src:0,stabl:3,start:3,step:2,stick:0,tag:0,tag_u:0,take:0,target:[2,3],term:3,test:2,than:0,thi:[0,1,3],thing:0,time:[1,3],tomorrow:1,too:2,topic:3,type:0,uerl:3,under:0,unsign:0,used:0,uses:[0,2],valu:0,virtual:3,welcom:1,were:0,what:[1,2],which:[0,3],wisdom:0,writer:0,written:3,you:2},titles:["Compact Term Encoding in BEAM","Contributing","Getting Started","\u03bcErlang \u2014 MicroErlang"],titleterms:{"\u03bcerlang":3,beam:0,compact:0,compil:2,contribut:1,encod:0,get:2,latest:3,microerlang:3,runtim:2,start:2,term:0,version:3}}) --------------------------------------------------------------------------------