Added first real test.

This commit is contained in:
Louis Burke 2025-03-16 19:11:23 -04:00
parent 1c69cbf70e
commit f382f8d575
18 changed files with 905 additions and 191 deletions

View file

@ -10,6 +10,6 @@ result can be more easily precompiled for native execution.
- overall philosophy - overall philosophy
- easy to target for compilers - easy to target for compilers
- easy to implement with overhead - easy to implement with overhead
- possible to implement with no overhead - possible to implement with no/low overhead
- can use easy but "slow" or DIY - can use easy but "slow" or DIY?
- e.g. MALL vs static memory and custom allocation - e.g. MALL vs static memory and custom allocation

View file

@ -1,6 +1,6 @@
\documentclass{article} \documentclass{article}
\usepackage[letterpaper, landscape, margin=1cm]{geometry} \usepackage[letterpaper, landscape, margin=0.7cm]{geometry}
\usepackage{tabularx} \usepackage{tabularx}
\usepackage{tabulary} \usepackage{tabulary}
@ -48,6 +48,8 @@
\newif\ifnoopdef \newif\ifnoopdef
\newif\ifopisfun \newif\ifopisfun
\newif\ifopisimm \newif\ifopisimm
\newif\ifopisgud
\newif\ifopisbad
\newcommand{\optextdescr}{} \newcommand{\optextdescr}{}
\newcommand{\optext}[1]{\renewcommand{\optextdescr}{#1}\noopdeffalse} \newcommand{\optext}[1]{\renewcommand{\optextdescr}{#1}\noopdeffalse}
@ -56,158 +58,223 @@
\noopdeftrue \noopdeftrue
\opisfunfalse \opisfunfalse
\opisimmfalse \opisimmfalse
\opisgudfalse
\opisbadfalse
% TODO: document and add more metasyntactic variables/functions % TODO: document and add more metasyntactic variables/functions?
% So far uncategorized instructions % So far uncategorized instructions
\if0#1
\optext{branch to immediate if x is not zero}
\stackdiagram{x,\ldots}{\ldots}
\fi
\if9#1
\optext{compare and swap x and y at p, push success}
\stackdiagram{y,x,p,\ldots}{z,\ldots}
\fi
\if E#1
\optext{round a to integer and store in x}
\stackdiagram{a,\ldots}{x,\ldots}
\fi
\if L#1
\optext{roll stack by immediate places}
\stackdiagram{z,\cdots,y,x,\ldots}{x,z,\cdots,y,\ldots}
\opisimmtrue
\fi
\if U#1
\optext{compare x to y unsigned and set z such that x o y is z o 0}
\stackdiagram{y,x,\ldots}{z,\ldots}
\fi
\if V#1
\optext{bitwise OR x and y}
\stackdiagram{y,x,\ldots}{x\vee{}y,\ldots}
\fi
\if W#1
\optext{truncate x to 32 bits, then sign extend}
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if d#1 \if d#1
\optext{set x bytes of memory to immediate value at p} \optext{set x bytes of memory to immediate value at p}
\opisbadtrue
\stackdiagram{x,p,\ldots}{\ldots} \stackdiagram{x,p,\ldots}{\ldots}
\fi \fi
\if e#1
\optext{rotate stack}
\stackdiagram{z,y,x,\ldots}{x,z,y,\ldots}
\fi
\if f#1 \if f#1
\optext{invoke f, saving return address on stack} \optext{invoke f, saving return address on stack}
\opisfuntrue \opisfuntrue
\stackdiagram{f,\ldots}{g,\ldots} \stackdiagram{f,\ldots}{g,\ldots}
\fi \fi
\if h#1
\optext{stack over}
\stackdiagram{y,x,\ldots}{x,y,x,\ldots}
\fi
\if j#1
\optext{negate a}
\stackdiagram{a,\ldots}{-a,\ldots}
\fi
\if k#1
\optext{stack pop}
\stackdiagram{x,\ldots}{\ldots}
\fi
\if l#1
\optext{bit clear y from x to z}
\stackdiagram{y,x,\ldots}{(x\wedge\neg{}y}
\fi
\if m#1
\optext{floating point difference}
\stackdiagram{b,a,\ldots}{a-b}
\fi
\if p#1
\optext{floating point multiply}
\stackdiagram{b,a,\ldots}{a\times{}b,\ldots}
\fi
\if q#1
\optext{floating point divide}
\stackdiagram{b,a,\ldots}{b\over{}a,\ldots}
\fi
\if s#1
\optext{floating point add}
\stackdiagram{b,a,\ldots}{b+a,\ldots}
\fi
\if t#1
\optext{stack swap}
\stackdiagram{y,x,\ldots}{x,y,\ldots}
\fi
\if u#1
\optext{logical shift x by immediate bits right}
\opisimmtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if v#1
\optext{floating point absolute value}
\stackdiagram{a,\ldots}{|a|,\ldots}
\fi
\if w#1
\optext{stack duplicate}
\stackdiagram{x,\ldots}{x,x,\ldots}
\fi
\ifx#1\_
\optext{negate x}
\stackdiagram{x,\ldots}{-x,\ldots}
\fi
\if .#1
\optext{convert x to a float}
\stackdiagram{x,\ldots}{a,\ldots}
\fi
\if +#1
\optext{integer add}
\stackdiagram{y,x,\ldots}{x+y,\ldots}
\fi
\if -#1
\optext{integer subtract}
\stackdiagram{y,x,\ldots}{x-y,\ldots}
\fi
\if *#1
\optext{integer multiply}
\stackdiagram{y,x,\ldots}{x\times{}y,\ldots}
\fi
\if /#1
\optext{integer divide}
\stackdiagram{y,x,\ldots}{x\div{}y,\ldots}
\fi
\ifx#1\%
\optext{integer modulo}
\stackdiagram{y,x,\ldots}{x\ \textrm{mod}\ y,\ldots}
\fi
\ifx#1\textbackslash
\optext{integer remainder}
\stackdiagram{y,x,\ldots}{x\%y,\ldots}
\fi
\if |#1
\optext{integer absolute value}
\stackdiagram{x,\ldots}{|x|,\ldots}
\fi
\ifx#1\$
\optext{load constant value}
\opisimmtrue
\stackdiagram{\ldots}{\star{},\ldots}
\fi
\if @#1 \if @#1
\optext{call immediate name} \optext{call immediate name}
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\opisgudtrue
\stackdiagram{\ldots}{f,\ldots} \stackdiagram{\ldots}{f,\ldots}
\fi \fi
\if `#1
\optext{call builtin with given name/value}
\opisimmtrue
\opisgudtrue
\stackdiagram{?,\ldots}{?,\ldots}
\fi
\if M#1
\optext{call math operation with given name/value}
\opisimmtrue
\opisgudtrue
\stackdiagram{?,\ldots}{?,\ldots}
\fi
% Memory management functions
\if F#1
\optext{free x bytes from p}
\stackdiagram{x,p,\ldots}{\ldots}
\fi
\if R#1
\optext{reallocate p to x bytes}
\stackdiagram{x,p,\ldots}{p,\ldots}
\fi
% Comparisons
\if9#1
\optext{compare and swap x and y at p, push success}
\stackdiagram{y,x,p,\ldots}{z,\ldots}
\fi
\if U#1
\optext{compare x to y unsigned and set z such that x o y is z o 0}
\opisgudtrue
\stackdiagram{y,x,\ldots}{z,\ldots}
\fi
\if ?#1
\optext{compare x to y and set z such that x o y is z o 0}
\opisgudtrue
\stackdiagram{x,y,\ldots}{z,\ldots}
\fi
\ifx#1\textasciitilde
\optext{compare a to b within an immediate error}
\opisgudtrue
\opisimmtrue
\stackdiagram{a,b,\ldots}{c,\ldots}
\fi
% Syntactical "instructions"
\ifx#1\textvisiblespace
\optext{do nothing}
\opisgudtrue
\stackdiagram{\ldots}{\ldots}
\fi
\ifx#1\#
\optext{comment}
\opisgudtrue
\stackdiagram{ }{ }
\fi
\ifx#1\P
\optext{mark a beat for relative branching}
\opisgudtrue
\stackdiagram{\ldots}{\ldots}
\fi
\if :#1
\optext{label line}
\opisgudtrue
\stackdiagram{\ldots}{\ldots}
\fi
\if '#1
\optext{breakpoint}
\opisgudtrue
\stackdiagram{ }{ }
\fi
\if "#1
\optext{change section}
\opisgudtrue
\stackdiagram{ }{ }
\fi
\ifx#1\$
\optext{push constant value}
\opisimmtrue
\opisgudtrue
\stackdiagram{\ldots}{\star{},\ldots}
\fi
% Stack manipulation instructions
\if L#1
\optext{roll stack by immediate places}
\opisgudtrue
\stackdiagram{z,\cdots,y,x,\ldots}{x,z,\cdots,y,\ldots}
\opisimmtrue
\fi
\if T#1
\optext{push top of stack down by immediate places}
\opisgudtrue
\stackdiagram{z,y,\cdots,x,\ldots}{z,\cdots,x,y,\ldots}
\opisimmtrue
\fi
\if e#1
\optext{rotate stack}
\opisgudtrue % named based on shape (take bottom to top)
\stackdiagram{z,y,x,\ldots}{x,z,y,\ldots}
\fi
\if h#1
\optext{stack over}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x,y,x,\ldots}
\fi
\if k#1
\optext{stack pop}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots}
\fi
\if t#1
\optext{stack swap}
\opisgudtrue % named after the Thrush combinator
\stackdiagram{y,x,\ldots}{x,y,\ldots}
\fi
\if w#1
\optext{stack duplicate}
\opisgudtrue % named after the Warbler combinator
\stackdiagram{x,\ldots}{x,x,\ldots}
\fi
\if (#1
\optext{set x to depth of stack}
\stackdiagram{\ldots}{x,\ldots}
\fi
\if )#1
\optext{pack x elements of stack into array p}
\stackdiagram{x,p,\ldots}{p,\ldots}
\fi
% Integer operations
\if V#1
\optext{bitwise OR x and y}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x\vee{}y,\ldots}
\fi
\if l#1
\optext{bit clear y from x to z}
\opisgudtrue
\stackdiagram{y,x,\ldots}{(x\wedge\neg{}y)}
\fi
\ifx#1\_
\optext{negate x}
\opisgudtrue
\stackdiagram{x,\ldots}{-x,\ldots}
\fi
\if +#1
\optext{integer add}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x+y,\ldots}
\fi
\if -#1
\optext{integer subtract}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x-y,\ldots}
\fi
\if *#1
\optext{integer multiply}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x\times{}y,\ldots}
\fi
\if /#1
\optext{integer divide}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x\div{}y,\ldots}
\fi
\ifx#1\%
\optext{integer modulo}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x\ \textrm{mod}\ y,\ldots}
\fi
\ifx#1\textbackslash
\optext{integer remainder}
\opisgudtrue
\stackdiagram{y,x,\ldots}{x\%y,\ldots}
\fi
\if |#1
\optext{integer absolute value}
\opisgudtrue
\stackdiagram{x,\ldots}{|x|,\ldots}
\fi
\ifx#1\& \ifx#1\&
\optext{set z to the bitwise and of x and y} \optext{set z to the bitwise and of x and y}
\opisgudtrue
\stackdiagram{x,y,\ldots}{x\wedge{}y} \stackdiagram{x,y,\ldots}{x\wedge{}y}
\fi \fi
\ifx#1\textasciicircum \ifx#1\textasciicircum
\optext{set z to the bitwise xor of x and y} \optext{set z to the bitwise xor of x and y}
\opisgudtrue
\stackdiagram{x,y,\ldots}{x\oplus{}y} \stackdiagram{x,y,\ldots}{x\oplus{}y}
\fi \fi
\if !#1 \if !#1
\optext{set y to the bitwise not of x} \optext{set y to the bitwise not of x}
\opisgudtrue
\stackdiagram{x,\ldots}{y,\ldots} \stackdiagram{x,\ldots}{y,\ldots}
\fi \fi
\if [#1 \if [#1
@ -220,68 +287,90 @@
\opisimmtrue \opisimmtrue
\stackdiagram{x,\ldots}{x,\ldots} \stackdiagram{x,\ldots}{x,\ldots}
\fi \fi
\if ?#1 \if u#1
\optext{compare x to y and set z such that x o y is z o 0} \optext{logical shift x by immediate bits right}
\stackdiagram{x,y,\ldots}{z,\ldots}
\fi
\ifx#1\textasciitilde
\optext{compare a to b within an immediate error}
\opisimmtrue \opisimmtrue
\stackdiagram{a,b,\ldots}{c,\ldots} \stackdiagram{x,\ldots}{x,\ldots}
\fi \fi
\if :#1
\optext{label code location} % Float operations
\stackdiagram{\ldots}{\ldots} \if j#1
\optext{negate a}
\opisgudtrue
\stackdiagram{a,\ldots}{-a,\ldots}
\fi \fi
\if (#1 \if m#1
\optext{set x to depth of stack} \optext{floating point difference}
\stackdiagram{\ldots}{x,\ldots} \opisgudtrue
\stackdiagram{b,a,\ldots}{a-b}
\fi \fi
\if )#1 \if p#1
\optext{pack x elements of stack into array p} \optext{floating point multiply}
\stackdiagram{x,p,\ldots}{p,\ldots} \opisgudtrue
\stackdiagram{b,a,\ldots}{a\times{}b,\ldots}
\fi \fi
\if '#1 \if q#1
\optext{embed data} \optext{floating point divide}
\stackdiagram{ }{ } \opisgudtrue
\stackdiagram{b,a,\ldots}{b\over{}a,\ldots}
\fi \fi
\if "#1 \if s#1
\optext{change section} \optext{floating point add}
\stackdiagram{ }{ } \opisgudtrue
\stackdiagram{b,a,\ldots}{b+a,\ldots}
\fi \fi
\if `#1 \if v#1
\optext{call builtin with given name/value} \optext{floating point absolute value}
\opisimmtrue \opisgudtrue
\stackdiagram{?,\ldots}{?,\ldots} \stackdiagram{a,\ldots}{|a|,\ldots}
\fi \fi
\ifx#1\textvisiblespace
\optext{do nothing} % Conversion instructions
\stackdiagram{\ldots}{\ldots} \if .#1
\optext{convert x to a float}
\opisgudtrue
\stackdiagram{x,\ldots}{a,\ldots}
\fi \fi
\ifx#1\# \if E#1
\optext{comment} \optext{round a to integer and store in x}
\stackdiagram{ }{ } \opisgudtrue
\stackdiagram{a,\ldots}{x,\ldots}
\fi \fi
\ifx#1\P \if W#1
\optext{mark a beat for relative branching} \optext{truncate x to 32 bits, then sign extend}
\stackdiagram{\ldots}{\ldots} \opisgudtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if S#1
\optext{truncate x to 16 bits, then sign extend}
\opisgudtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if O#1
\optext{truncate x to 8 bits, then sign extend}
\opisgudtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi \fi
% Load instructions % Load instructions
\if1#1 \if1#1
\optext{load byte x from p} \optext{load byte x from p}
\opisgudtrue
\stackdiagram{p,\ldots}{x,\ldots} \stackdiagram{p,\ldots}{x,\ldots}
\fi \fi
\if2#1 \if2#1
\optext{load short x from p} \optext{load short x from p}
\opisgudtrue
\stackdiagram{p,\ldots}{x,\ldots} \stackdiagram{p,\ldots}{x,\ldots}
\fi \fi
\if3#1 \if3#1
\optext{load int x from p} \optext{load int x from p}
\opisgudtrue
\stackdiagram{p,\ldots}{x,\ldots} \stackdiagram{p,\ldots}{x,\ldots}
\fi \fi
\if4#1 \if4#1
\optext{load word x from p} \optext{load word x from p}
\opisgudtrue
\stackdiagram{p,\ldots}{x,\ldots} \stackdiagram{p,\ldots}{x,\ldots}
\fi \fi
\if G#1 \if G#1
@ -303,122 +392,158 @@
% Store instructions % Store instructions
\if5#1 \if5#1
\optext{store byte x to p} \optext{store byte x to p}
\opisgudtrue
\stackdiagram{x,p,\ldots}{\ldots} \stackdiagram{x,p,\ldots}{\ldots}
\fi \fi
\if6#1 \if6#1
\optext{store short x to p} \optext{store short x to p}
\opisgudtrue
\stackdiagram{x,p,\ldots}{\ldots} \stackdiagram{x,p,\ldots}{\ldots}
\fi \fi
\if7#1 \if7#1
\optext{store int x to p} \optext{store int x to p}
\opisgudtrue
\stackdiagram{x,p,\ldots}{\ldots} \stackdiagram{x,p,\ldots}{\ldots}
\fi \fi
\if8#1 \if8#1
\optext{store word x to p} \optext{store word x to p}
\opisgudtrue
\stackdiagram{x,p,\ldots}{\ldots} \stackdiagram{x,p,\ldots}{\ldots}
\fi \fi
% Register instructions % Register instructions
\if A#1 \if A#1
\optext{store x in rA} \optext{store x in rA}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if B#1 \if B#1
\optext{store x in rB} \optext{store x in rB}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if C#1 \if C#1
\optext{store x in rC} \optext{store x in rC}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if I#1 \if I#1
\optext{store x in rI} \optext{store x in rI}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots}
\fi
\if N#1
\optext{store x in rN}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if X#1 \if X#1
\optext{store x in rX} \optext{store x in rX}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if Y#1 \if Y#1
\optext{store x in rY} \optext{store x in rY}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if Z#1 \if Z#1
\optext{store x in rZ} \optext{store x in rZ}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if a#1 \if a#1
\optext{load x from rA} \optext{load x from rA}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if b#1 \if b#1
\optext{load x from rB} \optext{load x from rB}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if c#1 \if c#1
\optext{load x from rC} \optext{load x from rC}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if i#1 \if i#1
\optext{load x from rI} \optext{load x from rI}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if n#1 \if n#1
\optext{load x from rN} \optext{load x from rN}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if x#1 \if x#1
\optext{load x from rX} \optext{load x from rX}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if y#1 \if y#1
\optext{load x from rY} \optext{load x from rY}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
\if z#1 \if z#1
\optext{load x from rZ} \optext{load x from rZ}
\opisgudtrue
\stackdiagram{\ldots}{x,\ldots} \stackdiagram{\ldots}{x,\ldots}
\fi \fi
% Branch instructions % Branch instructions
\if0#1
\optext{branch to immediate if x is not zero}
\opisgudtrue
\stackdiagram{x,\ldots}{\ldots}
\fi
\if <#1 \if <#1
\optext{branch to immediate if x is less than zero} \optext{branch to immediate if x is less than zero}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\ifx#1\{ \ifx#1\{
\optext{branch to immediate if x is less than or equal to zero} \optext{branch to immediate if x is less than or equal to zero}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if =#1 \if =#1
\optext{branch to immediate if x is zero} \optext{branch to immediate if x is zero}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\ifx#1\} \ifx#1\}
\optext{branch to immediate if x is greater or equal to zero} \optext{branch to immediate if x is greater or equal to zero}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if >#1 \if >#1
\optext{branch to immediate if x is greater than zero} \optext{branch to immediate if x is greater than zero}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{x,\ldots}{\ldots} \stackdiagram{x,\ldots}{\ldots}
\fi \fi
\if ,#1 \if ,#1
\optext{jump to immediate without pushing return address} \optext{jump to immediate without pushing return address}
\opisgudtrue
\opisimmtrue \opisimmtrue
\opisfuntrue \opisfuntrue
\stackdiagram{\ldots}{\ldots} \stackdiagram{\ldots}{\ldots}
\fi \fi
\if ;#1 \if ;#1
\optext{return from subroutine} \optext{return from subroutine}
\opisgudtrue
\opisfuntrue \opisfuntrue
\stackdiagram{f,\ldots}{\ldots} \stackdiagram{f,\ldots}{\ldots}
\fi \fi
@ -429,6 +554,14 @@
\stackdiagram{?}{?} \stackdiagram{?}{?}
\fi \fi
\ifopisgud
\tikzset{every node/.style={color=black!25!green}}
\fi
\ifopisbad
\tikzset{every node/.style={color=black!25!red}}
\fi
\ifopisfun \ifopisfun
\ifopisimm \ifopisimm
\tikzset{->/.style={arrows={Arc Barb[reversed,length=3pt]-Latex[length=5pt]}, double distance=1pt}} \tikzset{->/.style={arrows={Arc Barb[reversed,length=3pt]-Latex[length=5pt]}, double distance=1pt}}

View file

@ -89,6 +89,11 @@ Need better description of data '' format. See QBE's data for inspiration?
Consider having each op-code mean something different in different sections? Consider having each op-code mean something different in different sections?
Only consistency would be labels... those could be more universally parsed? Only consistency would be labels... those could be more universally parsed?
Alternatively remove sections altogether and use only dynamic memory? Alternatively remove sections altogether and use only dynamic memory?
I prefer sections, start with just implementations/documentation/specification
of code/data. For JIT code is obvious, data is variables loaded with appropriate
data and "passed through" to JIT.
Ideally keep immediate instructions consistent in each section (easier
parsing/highlighting/etc)?
``` ```
## File Format ## File Format

View file

@ -1,11 +1,9 @@
divert(`-1')dnl divert(`-1')dnl
changequote(`«', `»') changequote(`«', `»')
changecom(«/*», «*/») changecom(«\»)
\ note: use https://github.com/vim-scripts/syntaxm4.vim.git \ note: use https://github.com/vim-scripts/syntaxm4.vim.git
vim-m4-syntax: quote=«,» comment=\\ vim-m4-syntax: quote=«,» comment=\\
define(«TICK», changequote([,])[changequote([,])»changequote(«,»)]changequote(«,»))
\ forloop(var, from, to, stmt) - improved version: \ forloop(var, from, to, stmt) - improved version:
\ works even if VAR is not a strict macro name \ works even if VAR is not a strict macro name
\ performs sanity check that FROM is larger than TO \ performs sanity check that FROM is larger than TO

9
samples/hello.arx Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env arctic
"data"
:message sHello\ World!\x0A
"code"
@main
:main $15 $message $1 `1 $0 ;

View file

@ -11,12 +11,21 @@ let b:current_syntax = "arctic"
syntax match arcticComment "\v#.*$" syntax match arcticComment "\v#.*$"
highlight link arcticComment Comment highlight link arcticComment Comment
syntax region arcticData start=/'/ end=/'/ syntax region arcticEmbed start=/'/ end=/'/
highlight link arcticData Character highlight link arcticEmbed Character
syntax region arcticSection start=/"/ end=/"/ syntax region arcticSection start=/"/ end=/"/
highlight link arcticSection PreProc highlight link arcticSection PreProc
syntax region arcticData keepend start=/\c"data"/ end=/"/ contains=arcticData.*,arcticLabel,arcticSection
syntax region arcticCode keepend start=/\c"code"/ end=/"/ contains=arcticCode.*,arcticLabel,arcticCall,arcticConstant,arcticSection
syntax match arcticDataImmediate "\v[][a-zA-Z0-9_]([^ ]|\\ )*"
highlight link arcticDataImmediate Statement
syntax match arcticDataOperator "\v[-.+*/%\|;&^!?()]"
highlight link arcticDataOperator Operator
syntax match arcticLabel "\v:[^ ]*" syntax match arcticLabel "\v:[^ ]*"
highlight link arcticLabel Label highlight link arcticLabel Label
@ -26,8 +35,8 @@ highlight link arcticCall Function
syntax match arcticConstant "\v\$[^ ]*" syntax match arcticConstant "\v\$[^ ]*"
highlight link arcticConstant Constant highlight link arcticConstant Constant
syntax match arcticImmediate "\v[][0EFGHLMPQTdou$@<{=}>,`][^ ]*" syntax match arcticCodeImmediate "\v[][0EFGHLMPQTdou@<{=}>,`]([^ ]|\\ )*"
highlight link arcticImmediate Statement highlight link arcticCodeImmediate Statement
syntax match arcticOperator "\v[-123456789ABCDIJKNORSUVWXYZabcefghijklmnpqrstvwxyz_.+*/%\|;&^!?()]" syntax match arcticCodeOperator "\v[-123456789ABCDIJKNORSUVWXYZabcefghijklmnpqrstvwxyz_.+*/%\|;&^!?()]"
highlight link arcticOperator Operator highlight link arcticCodeOperator Operator

View file

@ -1,3 +1,37 @@
#include <string.h>
#include "architectures.h" #include "architectures.h"
#include "arch/x86_64.h" enum Architecture parse_arch(const char *arch) {
if (!arch)
return ARCH_NATIVE;
if (!strncmp(arch, "x86_64", 6)) {
return ARCH_X86_64;
}
return ARCH_NATIVE;
}
const char *archstr(enum Architecture arch) {
switch (arch) {
case ARCH_NATIVE:
return "native";
case ARCH_INTERPRET:
return "interpret";
case ARCH_X86_64:
return "x86_64";
}
return 0;
}
enum Architecture getnative() {
#ifdef __x86_64__
return ARCH_X86_64;
#else
return ARCH_INTERPRET;
#endif
}

View file

@ -2,7 +2,17 @@
#define ARCHITECTURES_H #define ARCHITECTURES_H
enum Architecture { enum Architecture {
ARCH_NATIVE ARCH_NATIVE,
ARCH_INTERPRET,
ARCH_X86_64
}; };
/* when given a null pointer, defaults to ARCH_NATIVE */
enum Architecture parse_arch(const char *arch);
const char *archstr(enum Architecture arch);
enum Architecture getnative();
#endif /* ARCHITECTURES_H */ #endif /* ARCHITECTURES_H */

53
src/arctic.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef ARCTIC_H
#define ARCTIC_H
#include <stdint.h>
enum ArcticVariableIndex {
VARIABLE_A = 0, VARIABLE_B, VARIABLE_C,
VARIABLE_I, VARIABLE_N,
VARIABLE_X, VARIABLE_Y, VARIABLE_Z,
VARIABLE_MAX
};
enum ArcticImmediateKind {
PLAIN_IMMEDIATE, VARIABLE_IMMEDIATE, INTEGER_IMMEDIATE, NUMERIC_IMMEDIATE, SYMBOLIC_IMMEDIATE
};
struct ArcticImmediate {
enum ArcticImmediateKind kind;
union {
enum ArcticVariableIndex v;
int64_t i;
double n;
const char *s;
};
};
struct ArcticOperation {
char code;
uint8_t bigendian;
struct ArcticImmediate immediate;
};
union ArcticType {
int8_t b;
uint8_t ub;
int16_t s;
uint16_t us;
int32_t w;
uint32_t uw;
int64_t i;
uint64_t u;
double d;
uint8_t *p;
struct ArcticOperation *f;
};
struct ArcticState {
};
int parse_arctic(struct ArcticState *state, const char *arctic);
#endif /* ARCTIC_H */

View file

@ -1,10 +1,29 @@
#include "args.h" #include "args.h"
#include <argp.h> #include <argp.h>
#include <string.h>
static int parse_opt(int key, char *arg, struct argp_state *state) { static int parse_opt(int key, char *arg, struct argp_state *state) {
switch (key) { struct Arguments *args = state->input;
switch (key) {
case 'i':
args->behaviour = BEHAVIOUR_INTERPRET;
return 0;
case 'j':
args->behaviour = BEHAVIOUR_JIT;
args->jit.arch = parse_arch(arg);
return 0;
case 'a':
args->behaviour = BEHAVIOUR_ASM;
args->assemble.arch = parse_arch(arg);
return 0;
case ARGP_KEY_ARG:
strncpy(args->program, arg, PATH_MAX);
return 0;
} }
return 0; return 0;
@ -21,5 +40,7 @@ int parse_arguments(struct Arguments *args, int argc, char *argv[]) {
const char *args_doc = "PROGRAM"; const char *args_doc = "PROGRAM";
struct argp argp = { options, parse_opt, args_doc, 0, 0, 0 }; struct argp argp = { options, parse_opt, args_doc, 0, 0, 0 };
return argp_parse(&argp, argc, argv, 0, 0, args); int result;
argp_parse(&argp, argc, argv, 0, &result, args);
return result;
} }

View file

@ -3,7 +3,7 @@
#include "architectures.h" #include "architectures.h"
#include <stdio.h> #include <linux/limits.h>
enum Behaviour { enum Behaviour {
BEHAVIOUR_INTERPRET, BEHAVIOUR_INTERPRET,
@ -13,12 +13,13 @@ enum Behaviour {
struct Arguments { struct Arguments {
enum Behaviour behaviour; enum Behaviour behaviour;
FILE *program; char program[PATH_MAX + 1];
union { union {
struct { struct {
} interpret; } interpret;
struct { struct {
enum Architecture arch;
} jit; } jit;
struct { struct {

301
src/critbit.c Normal file
View file

@ -0,0 +1,301 @@
#include "critbit.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
struct CritBitNode {
unsigned int isleaf:1;
union {
struct {
unsigned int byte:31;
uint8_t others;
struct CritBitNode *left; /* lefts are 0s */
struct CritBitNode *right; /* rights are 1s */
};
struct {
unsigned int valuelen:31;
void *value;
char *key;
};
};
};
void crit_bit_init(struct CritBitTree *tree, void (*free_data)(void *data, int datalen)) {
if (!tree) return;
tree->count = 0;
tree->root = 0;
tree->free_data = free_data;
}
static void free_node(void (*free_data)(void *data, int datalen), struct CritBitNode *node) {
if (!node) return;
if (node->isleaf) {
if (free_data)
free_data(node->value, node->valuelen);
free(node->key);
free(node);
} else {
free_node(free_data, node->left);
free_node(free_data, node->right);
free(node);
}
}
void crit_bit_free(struct CritBitTree *tree) {
if (tree->root)
free_node(tree->free_data, tree->root);
tree->root = 0;
tree->count = 0;
}
static struct CritBitNode *makeleaf(const char *key, size_t keylen, void *data, int datalen) {
struct CritBitNode *leaf = malloc(sizeof *leaf);
if (!leaf)
return 0;
leaf->key = malloc(keylen + 1);
if (!leaf->key) {
free(leaf);
return 0;
}
memcpy(leaf->key, key, keylen + 1);
leaf->value = data;
leaf->valuelen = datalen;
leaf->isleaf = 1;
return leaf;
}
int crit_bit_add(struct CritBitTree *tree, const char *key, void *data, int datalen) {
if (!tree) return 0;
const uint8_t *keybytes = (void*)key;
size_t keylen = strlen(key);
struct CritBitNode *node = tree->root;
if (!node) {
struct CritBitNode *root = malloc(sizeof *root);
root->key = malloc(keylen + 1);
strcpy(root->key, key);
root->valuelen = datalen;
root->value = data;
root->isleaf = 1;
tree->root = root;
tree->count++;
return 0;
}
while (node && !node->isleaf) {
uint8_t check = 0;
if (node->byte < keylen)
check = keybytes[node->byte];
if ((node->others | check) == 0xFF)
node = node->right;
else
node = node->left;
}
if (!node)
return -1;
uint32_t newbyte;
uint32_t newothers = ~0; /* sigil for finding existing keys */
for (newbyte = 0; newbyte < keylen; ++newbyte) {
if (node->key[newbyte] != key[newbyte]) {
newothers = node->key[newbyte] ^ key[newbyte];
break;
}
}
if (newothers == ~0) {
if (node->key[newbyte] != 0) {
newothers = node->key[newbyte];
}
}
if (newothers == ~0) {
if (tree->free_data)
tree->free_data(node->value, node->valuelen);
node->value = data;
node->valuelen = datalen;
return 0;
}
/* bit hacks */
while (newothers & (newothers - 1))
newothers &= newothers - 1;
newothers ^= 0xFF;
uint8_t c = node->key[newbyte];
int isright = (1 + (newothers | c)) >> 8;
struct CritBitNode *newnode = malloc(sizeof *newnode);
if (!newnode)
return -2;
struct CritBitNode *leafnode = makeleaf(key, keylen, data, datalen);
if (!leafnode)
return -2;
newnode->isleaf = 0;
newnode->byte = newbyte;
newnode->others = newothers;
if (isright) {
newnode->left = leafnode;
} else {
newnode->right = leafnode;
}
struct CritBitNode **parent = (struct CritBitNode **)&tree->root;
for (;;) {
struct CritBitNode *here = *parent;
if (here->isleaf) break;
if (here->byte > newbyte) break;
if (here->byte == newbyte && here->others > newothers) break;
uint8_t c2 = 0;
if (here->byte < keylen) c2 = keybytes[here->byte];
if ((1 + (here->others | c2)) > 0xFF) {
parent = &here->right;
} else {
parent = &here->left;
}
}
if (isright) {
newnode->right = *parent;
} else {
newnode->left = *parent;
}
*parent = newnode;
tree->count++;
return 0;
}
int crit_bit_has(const struct CritBitTree *tree, const char *key) {
if (!tree) return 0;
const uint8_t *keybytes = (void*)key;
size_t keylen = strlen(key);
const struct CritBitNode *node = tree->root;
while (node && !node->isleaf) {
uint8_t check = 0;
if (node->byte < keylen)
check = keybytes[node->byte];
if ((node->others | check) == 0xFF)
node = node->right;
else
node = node->left;
}
return node && !strcmp(key, node->key);
}
void *crit_bit_get(const struct CritBitTree *tree, const char *key, int *len) {
if (!tree) return 0;
const uint8_t *keybytes = (void*)key;
size_t keylen = strlen(key);
const struct CritBitNode *node = tree->root;
while (node && !node->isleaf) {
uint8_t check = 0;
if (node->byte < keylen)
check = keybytes[node->byte];
if ((node->others | check) == 0xFF)
node = node->right;
else
node = node->left;
}
if (!node) return 0;
if (len)
*len = node->valuelen;
return node->value;
}
int crit_bit_del(struct CritBitTree *tree, const char *key) {
if (!tree) return 1;
const uint8_t *keybytes = (void*)key;
size_t keylen = strlen(key);
struct CritBitNode *node = tree->root;
struct CritBitNode *prev = 0;
void *tmp = &(tree->root);
struct CritBitNode **parent = (struct CritBitNode **)tmp;
struct CritBitNode **grandparent = 0;
int isright;
if (!node) return 1;
while (node && !node->isleaf) {
grandparent = parent;
uint8_t c = 0;
if (node->byte < keylen) c = keybytes[node->byte];
isright = ((1 + (node->others | c)) > 0xFF);
if (isright) {
parent = &node->right;
} else {
parent = &node->left;
}
prev = node;
node = *parent;
}
if (!node)
return 1;
if (strcmp(key, node->key))
return 1;
if (tree->free_data) tree->free_data(node->value, node->valuelen);
free(node->key);
free(node);
tree->count--;
if (!grandparent || !prev) {
tree->root = 0;
return 0;
}
if (isright) {
*grandparent = prev->left;
} else {
*grandparent = prev->right;
}
free(prev);
return 0;
}
int crit_bit_size(const struct CritBitTree *tree) {
return tree->count;
}
static int iterate_node(int (*callback)(const char*,void*,int,void*), void *data, struct CritBitNode *node) {
if (!node) return 0;
if (node->isleaf) {
return callback(node->key, node->value, node->valuelen, data);
} else {
int r;
r = iterate_node(callback, data, node->left);
if (r) return r;
r = iterate_node(callback, data, node->right);
return r;
}
}
int crit_bit_foreach(const struct CritBitTree *tree, int (*callback)(const char*,void*,int,void*), void *data) {
if (!tree) return 0;
if (!tree->root) return 0;
if (!callback) return 0;
return iterate_node(callback, data, tree->root);
}

19
src/critbit.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CRITBIT_H
#define CRITBIT_H
struct CritBitTree {
void *root;
int count;
void (*free_data)(void *data, int datalen);
};
void crit_bit_init(struct CritBitTree *tree, void (*free_data)(void *data, int datalen)); /* or just { 0 } if you don't need a free_data */
void crit_bit_free(struct CritBitTree *tree); /* a free tree is still initted and can be used */
int crit_bit_add(struct CritBitTree *tree, const char *key, void *data, int datalen);
int crit_bit_has(const struct CritBitTree *tree, const char *key);
void *crit_bit_get(const struct CritBitTree *tree, const char *key, int *len);
int crit_bit_del(struct CritBitTree *tree, const char *key);
int crit_bit_size(const struct CritBitTree *tree);
int crit_bit_foreach(const struct CritBitTree *tree, int (*callback)(const char*,void*,int,void*), void *data); /* returns short-circuit OR of callback value */
#endif /* CRITBIT_H */

View file

@ -1,7 +1,4 @@
#include "jit.h" #include "jit.h"
#ifdef __x86_64__ // #include "arch/x86_64.c"
#include "jit/x86_64.c" // #include "arch/interpret.c"
#else
#include "jit/interpret.c"
#endif /* architecture */

View file

@ -1,7 +1,19 @@
#ifndef JIT_H #ifndef JIT_H
#define JIT_H #define JIT_H
/* the implementations must implement this function */ #include "architectures.h"
int (*jit(const char *src))(int, char**);
/* these are what jit functions return */
typedef int (*main_func)(int, char**);
/* the implementations must implement these functions */
typedef int (*((*jit)(const char *,int)))(int, char**);
jit get_implementation(enum Architecture arch);
/* these are what the asm functions receive */
typedef void (*str_func)(const char *, void *);
void asm_implementation(enum Architecture arch, const char *source, int len, str_func cb, void *data);
#endif /* JIT_H */ #endif /* JIT_H */

View file

@ -21,7 +21,7 @@ const uint8_t prelude[] = {
// MOV(RBP,RSP) // MOV(RBP,RSP)
}; };
int (*jit(const char *src))(int, char**) { int (*jit_x86_64(const char *src))(int, char**) {
// TODO: see https://github.com/spencertipping/jit-tutorial // TODO: see https://github.com/spencertipping/jit-tutorial
} }

View file

@ -1,14 +1,120 @@
#include <stdio.h> #include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <argp.h> #include <argp.h>
#include "jit.h" #include "jit.h"
#include "args.h" #include "args.h"
#include "architectures.h" #include "architectures.h"
int main(int argc, char *argv[]) { char *read_dynamic(int fd, int *len) {
struct Arguments args; char buf[0x1000];
int rc = parse_arguments(&args, argc, argv); ssize_t n;
char *result = 0;
*len = 0;
while ((n = read(fd, buf, sizeof buf))) {
if (n < 0) {
if (errno == EAGAIN)
continue;
perror("read");
break;
}
printf("%d\n", rc); char *nresult = realloc(result, *len + n + 1);
return 0; if (!nresult) {
free(result);
return 0;
}
result = nresult;
memcpy(result + *len, buf, n);
*len += n;
result[*len] = 0;
}
return result;
}
char *read_filename(const char *fname, int *len, int *mapped) {
if (fname[0] == '-' && fname[1] == 0) {
*mapped = 0;
return read_dynamic(0, len);
} else {
char *result = 0;
int fd = open(fname, O_RDONLY);
*len = lseek(fd, 0, SEEK_END);
if (*len < 0) {
/* fallback to dynamic memory */
*len = 0;
*mapped = 0;
result = read_dynamic(fd, len);
} else {
*mapped = 1;
result = mmap(0, *len, PROT_READ, MAP_PRIVATE, fd, 0);
}
close(fd);
return result;
}
}
void free_read_file(char *contents, int len, int mapped) {
if (!mapped) {
free(contents);
} else {
munmap(contents, len);
}
}
void dump_str(const char *str, void *unused) {
puts(str);
}
int main(int argc, char *argv[]) {
struct Arguments args = { 0 };
int offset = parse_arguments(&args, argc, argv);
int len;
int mapped;
char *source = read_filename(args.program, &len, &mapped);
int result;
switch (args.behaviour) {
case BEHAVIOUR_INTERPRET: {
jit interp = get_implementation(ARCH_INTERPRET);
if (!interp) {
free_read_file(source, len, mapped);
return -1;
}
main_func interp_main = interp(source, len);
free_read_file(source, len, mapped);
if (!interp_main) return -1;
result = interp_main(argc - offset, argv + offset);
break;
}
case BEHAVIOUR_JIT: {
jit jitter = get_implementation(args.jit.arch);
if (!jitter) {
free_read_file(source, len, mapped);
return -1;
}
main_func jitter_main = jitter(source, len);
free_read_file(source, len, mapped);
result = jitter_main(argc - offset, argv + offset);
break;
}
case BEHAVIOUR_ASM:
asm_implementation(args.assemble.arch, source, len, dump_str, 0);
free_read_file(source, len, mapped);
result = 0;
break;
}
return result;
} }

6
tests/test0.arx Normal file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env arctic
# Simple smoke test, ensures shebang, comments, and basic "code" work
"code"
$0 ;