<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 074wait.cc</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.SalientComment { color: #00ffff; }
.muRecipe { color: #ff8700; }
.muData { color: #ffff00; }
.LineNr { color: #444444; }
.traceContains { color: #008000; }
.Delimiter { color: #800080; }
.Normal { color: #aaaaaa; background-color: #080808; padding-bottom: 1px; }
.cSpecial { color: #008000; }
.Conceal { color: #4e4e4e; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
.Constant { color: #00a0a0; }
.Special { color: #c00000; }
.Identifier { color: #c0a020; }
-->
</style>
<script type='text/javascript'>
<!--
/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
var lineNum;
lineNum = window.location.hash;
lineNum = lineNum.substr(1); /* strip off '#' */
if (lineNum.indexOf('L') == -1) {
lineNum = 'L'+lineNum;
}
lineElem = document.getElementById(lineNum);
/* Always jump to new location even if the line was hidden inside a fold, or
* we corrected the raw number to a line ID.
*/
if (lineElem) {
lineElem.scrollIntoView(true);
}
return true;
}
if ('onhashchange' in window) {
window.onhashchange = JumpToLine;
}
-->
</script>
</head>
<body onload='JumpToLine();'>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr"> 1 </span><span class="Comment">//: Routines can be put in a 'waiting' state, from which it will be ready to</span>
<span id="L2" class="LineNr"> 2 </span><span class="Comment">//: run again when a specific memory location changes its value. This is Mu's</span>
<span id="L3" class="LineNr"> 3 </span><span class="Comment">//: basic technique for orchestrating the order in which different routines</span>
<span id="L4" class="LineNr"> 4 </span><span class="Comment">//: operate.</span>
<span id="L5" class="LineNr"> 5 </span>
<span id="L6" class="LineNr"> 6 </span><span class="Delimiter">:(scenario wait_for_location)</span>
<span id="L7" class="LineNr"> 7 </span><span class="muRecipe">def</span> f1 [
<span id="L8" class="LineNr"> 8 </span> <span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span id="L9" class="LineNr"> 9 </span> start-running f2
<span id="L10" class="LineNr"> 10 </span> <span class="Constant">20</span>:location<span class="Special"> <- </span>copy <span class="Constant">10</span>/unsafe
<span id="L11" class="LineNr"> 11 </span> wait-<span class="Normal">for</span>-reset-then-set <span class="Constant">20</span>:location
<span id="L12" class="LineNr"> 12 </span> <span class="Comment"># wait for f2 to run and reset location 1</span>
<span id="L13" class="LineNr"> 13 </span> <span class="Constant">30</span>:num<span class="Special"> <- </span>copy <span class="Constant">10</span>:num
<span id="L14" class="LineNr"> 14 </span>]
<span id="L15" class="LineNr"> 15 </span><span class="muRecipe">def</span> f2 [
<span id="L16" class="LineNr"> 16 </span> <span class="Constant">10</span>:location<span class="Special"> <- </span>copy <span class="Constant">0</span>/unsafe
<span id="L17" class="LineNr"> 17 </span>]
<span id="L18" class="LineNr"> 18 </span><span class="traceContains">+schedule: f1</span>
<span id="L19" class="LineNr"> 19 </span><span class="traceContains">+run: waiting for location 10 to <a href='000organization.cc.html#L134'>reset</a></span>
<span id="L20" class="LineNr"> 20 </span><span class="traceContains">+schedule: f2</span>
<span id="L21" class="LineNr"> 21 </span><span class="traceContains">+schedule: waking up routine 1</span>
<span id="L22" class="LineNr"> 22 </span><span class="traceContains">+schedule: f1</span>
<span id="L23" class="LineNr"> 23 </span><span class="traceContains">+mem: storing 1 in location 30</span>
<span id="L24" class="LineNr"> 24 </span>
<span id="L25" class="LineNr"> 25 </span><span class="Comment">//: define the new state that all routines can be in</span>
<span id="L26" class="LineNr"> 26 </span>
<span id="L27" class="LineNr"> 27 </span><span class="Delimiter">:(before "End routine States")</span>
<span id="L28" class="LineNr"> 28 </span>WAITING<span class="Delimiter">,</span>
<span id="L29" class="LineNr"> 29 </span><span class="Delimiter">:(before "End routine Fields")</span>
<span id="L30" class="LineNr"> 30 </span><span class="Comment">// only if state == WAITING</span>
<span id="L31" class="LineNr"> 31 </span><span class="Normal">int</span> waiting_on_location<span class="Delimiter">;</span>
<span id="L32" class="LineNr"> 32 </span><span class="Delimiter">:(before "End routine Constructor")</span>
<span id="L33" class="LineNr"> 33 </span>waiting_on_location = <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L34" class="LineNr"> 34 </span>
<span id="L35" class="LineNr"> 35 </span><span class="Delimiter">:(before "End Mu Test Teardown")</span>
<span id="L36" class="LineNr"> 36 </span><span class="Normal">if</span> <span class="Delimiter">(</span>Passed && <a href='074wait.cc.html#L47'>any_routines_waiting</a><span class="Delimiter">())</span>
<span id="L37" class="LineNr"> 37 </span> <a href='003trace.cc.html#L168'>raise</a> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": deadlock!</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L38" class="LineNr"> 38 </span><span class="Delimiter">:(before "End Run Routine")</span>
<span id="L39" class="LineNr"> 39 </span><span class="Normal">if</span> <span class="Delimiter">(</span><a href='074wait.cc.html#L47'>any_routines_waiting</a><span class="Delimiter">())</span> <span class="Delimiter">{</span>
<span id="L40" class="LineNr"> 40 </span> <a href='003trace.cc.html#L168'>raise</a> << <span class="Constant">"deadlock!</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L41" class="LineNr"> 41 </span> <a href='074wait.cc.html#L54'>dump_waiting_routines</a><span class="Delimiter">();</span>
<span id="L42" class="LineNr"> 42 </span><span class="Delimiter">}</span>
<span id="L43" class="LineNr"> 43 </span><span class="Delimiter">:(before "End Test Teardown")</span>
<span id="L44" class="LineNr"> 44 </span><span class="Normal">if</span> <span class="Delimiter">(</span>Passed && any_routines_with_error<span class="Delimiter">())</span>
<span id="L45" class="LineNr"> 45 </span> <a href='003trace.cc.html#L168'>raise</a> << <span class="Constant">"some routines died with errors</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L46" class="LineNr"> 46 </span><span class="Delimiter">:(code)</span>
<span id="L47" class="LineNr"> 47 </span><span class="Normal">bool</span> <a href='074wait.cc.html#L47'>any_routines_waiting</a><span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span id="L48" class="LineNr"> 48 </span> <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < <a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L49" class="LineNr"> 49 </span> <span class="Conceal">¦</span> <span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state == WAITING<span class="Delimiter">)</span>
<span id="L50" class="LineNr"> 50 </span> <span class="Conceal">¦</span> <span class="Conceal">¦</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
<span id="L51" class="LineNr"> 51 </span> <span class="Delimiter">}</span>
<span id="L52" class="LineNr"> 52 </span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span id="L53" class="LineNr"> 53 </span><span class="Delimiter">}</span>
<span id="L54" class="LineNr"> 54 </span><span class="Normal">void</span> <a href='074wait.cc.html#L54'>dump_waiting_routines</a><span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span id="L55" class="LineNr"> 55 </span> <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < <a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L56" class="LineNr"> 56 </span> <span class="Conceal">¦</span> <span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state == WAITING<span class="Delimiter">)</span>
<span id="L57" class="LineNr"> 57 </span> <span class="Conceal">¦</span> <span class="Conceal">¦</span> cerr << i << <span class="Constant">": "</span> << <a href='073scheduler.cc.html#L110'>routine_label</a><span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
<span id="L58" class="LineNr"> 58 </span> <span class="Delimiter">}</span>
<span id="L59" class="LineNr"> 59 </span><span class="Delimiter">}</span>
<span id="L60" class="LineNr"> 60 </span>
<span id="L61" class="LineNr"> 61 </span><span class="Delimiter">:(scenario wait_for_location_can_deadlock)</span>
<span id="L62" class="LineNr"> 62 </span><span class="Special">% Hide_errors = true;</span>
<span id="L63" class="LineNr"> 63 </span><span class="muRecipe">def</span> <a href='000organization.cc.html#L113'>main</a> [
<span id="L64" class="LineNr"> 64 </span> <span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">1</span>
<span id="L65" class="LineNr"> 65 </span> <span class="Constant">20</span>:location<span class="Special"> <- </span>copy <span class="Constant">10</span>/unsafe
<span id="L66" class="LineNr"> 66 </span> wait-<span class="Normal">for</span>-reset-then-set <span class="Constant">20</span>:location
<span id="L67" class="LineNr"> 67 </span>]
<span id="L68" class="LineNr"> 68 </span><span class="traceContains">+error: deadlock!</span>
<span id="L69" class="LineNr"> 69 </span>
<span id="L70" class="LineNr"> 70 </span><span class="Comment">//: Primitive recipe to put routines in that state.</span>
<span id="L71" class="LineNr"> 71 </span><span class="Comment">//: This primitive is also known elsewhere as compare-and-set (CAS). Used to</span>
<span id="L72" class="LineNr"> 72 </span><span class="Comment">//: build locks.</span>
<span id="L73" class="LineNr"> 73 </span>
<span id="L74" class="LineNr"> 74 </span><span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
<span id="L75" class="LineNr"> 75 </span>WAIT_FOR_RESET_THEN_SET<span class="Delimiter">,</span>
<span id="L76" class="LineNr"> 76 </span><span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
<span id="L77" class="LineNr"> 77 </span><a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"wait-for-reset-then-set"</span><span class="Delimiter">,</span> WAIT_FOR_RESET_THEN_SET<span class="Delimiter">);</span>
<span id="L78" class="LineNr"> 78 </span><span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span id="L79" class="LineNr"> 79 </span><span class="Normal">case</span> WAIT_FOR_RESET_THEN_SET: <span class="Delimiter">{</span>
<span id="L80" class="LineNr"> 80 </span> <span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L81" class="LineNr"> 81 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-reset-then-set' requires exactly one ingredient, but got '"</span> << to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L82" class="LineNr"> 82 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L83" class="LineNr"> 83 </span> <span class="Delimiter">}</span>
<span id="L84" class="LineNr"> 84 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
<span id="L85" class="LineNr"> 85 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-reset-then-set' requires a location ingredient, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L86" class="LineNr"> 86 </span> <span class="Delimiter">}</span>
<span id="L87" class="LineNr"> 87 </span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L88" class="LineNr"> 88 </span><span class="Delimiter">}</span>
<span id="L89" class="LineNr"> 89 </span><span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span id="L90" class="LineNr"> 90 </span><span class="Normal">case</span> WAIT_FOR_RESET_THEN_SET: <span class="Delimiter">{</span>
<span id="L91" class="LineNr"> 91 </span> <span class="Normal">int</span> loc = <span class="Normal">static_cast</span><<span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
<span id="L92" class="LineNr"> 92 </span> <a href='003trace.cc.html#L161'>trace</a><span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"wait: *"</span> << loc << <span class="Constant">" = "</span> << <a href='001help.cc.html#L228'>get_or_insert</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L93" class="LineNr"> 93 </span> <span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L228'>get_or_insert</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L94" class="LineNr"> 94 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L161'>trace</a><span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"location "</span> << loc << <span class="Constant">" is already 0; setting"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L95" class="LineNr"> 95 </span> <span class="Conceal">¦</span> <a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">1</span><span class="Delimiter">);</span>
<span id="L96" class="LineNr"> 96 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L97" class="LineNr"> 97 </span> <span class="Delimiter">}</span>
<span id="L98" class="LineNr"> 98 </span> <a href='003trace.cc.html#L161'>trace</a><span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"waiting for location "</span> << loc << <span class="Constant">" to reset"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L99" class="LineNr"> 99 </span> Current_routine<span class="Delimiter">-></span>state = WAITING<span class="Delimiter">;</span>
<span id="L100" class="LineNr">100 </span> Current_routine<span class="Delimiter">-></span>waiting_on_location = loc<span class="Delimiter">;</span>
<span id="L101" class="LineNr">101 </span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L102" class="LineNr">102 </span><span class="Delimiter">}</span>
<span id="L103" class="LineNr">103 </span>
<span id="L104" class="LineNr">104 </span><span class="Comment">//: Counterpart to unlock a lock.</span>
<span id="L105" class="LineNr">105 </span><span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
<span id="L106" class="LineNr">106 </span>RESET<span class="Delimiter">,</span>
<span id="L107" class="LineNr">107 </span><span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
<span id="L108" class="LineNr">108 </span><a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"reset"</span><span class="Delimiter">,</span> RESET<span class="Delimiter">);</span>
<span id="L109" class="LineNr">109 </span><span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span id="L110" class="LineNr">110 </span><span class="Normal">case</span> RESET: <span class="Delimiter">{</span>
<span id="L111" class="LineNr">111 </span> <span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L112" class="LineNr">112 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'reset' requires exactly one ingredient, but got '"</span> << to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L113" class="LineNr">113 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L114" class="LineNr">114 </span> <span class="Delimiter">}</span>
<span id="L115" class="LineNr">115 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
<span id="L116" class="LineNr">116 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'reset' requires a location ingredient, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L117" class="LineNr">117 </span> <span class="Delimiter">}</span>
<span id="L118" class="LineNr">118 </span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L119" class="LineNr">119 </span><span class="Delimiter">}</span>
<span id="L120" class="LineNr">120 </span><span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span id="L121" class="LineNr">121 </span><span class="Normal">case</span> RESET: <span class="Delimiter">{</span>
<span id="L122" class="LineNr">122 </span> <span class="Normal">int</span> loc = <span class="Normal">static_cast</span><<span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
<span id="L123" class="LineNr">123 </span> <a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">0</span><span class="Delimiter">);</span>
<span id="L124" class="LineNr">124 </span> <a href='003trace.cc.html#L161'>trace</a><span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"reset: *"</span> << loc << <span class="Constant">" = "</span> << <a href='001help.cc.html#L228'>get_or_insert</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L125" class="LineNr">125 </span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L126" class="LineNr">126 </span><span class="Delimiter">}</span>
<span id="L127" class="LineNr">127 </span>
<span id="L128" class="LineNr">128 </span><span class="Comment">//: scheduler tweak to get routines out of that state</span>
<span id="L129" class="LineNr">129 </span>
<span id="L130" class="LineNr">130 </span><span class="Delimiter">:(before "End Scheduler State Transitions")</span>
<span id="L131" class="LineNr">131 </span><span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < <a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L132" class="LineNr">132 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state != WAITING<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span id="L133" class="LineNr">133 </span> <span class="Normal">int</span> loc = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>waiting_on_location<span class="Delimiter">;</span>
<span id="L134" class="LineNr">134 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>loc && <a href='001help.cc.html#L228'>get_or_insert</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L135" class="LineNr">135 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L161'>trace</a><span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"schedule"</span><span class="Delimiter">)</span> << <span class="Constant">"waking up routine "</span> << Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>id << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L136" class="LineNr">136 </span> <span class="Conceal">¦</span> <a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">1</span><span class="Delimiter">);</span>
<span id="L137" class="LineNr">137 </span> <span class="Conceal">¦</span> Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state = <a href='073scheduler.cc.html#L41'>RUNNING</a><span class="Delimiter">;</span>
<span id="L138" class="LineNr">138 </span> <span class="Conceal">¦</span> Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>waiting_on_location = <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L139" class="LineNr">139 </span> <span class="Delimiter">}</span>
<span id="L140" class="LineNr">140 </span><span class="Delimiter">}</span>
<span id="L141" class="LineNr">141 </span>
<span id="L142" class="LineNr">142 </span><span class="Comment">//: Primitive to help compute locations to wait on.</span>
<span id="L143" class="LineNr">143 </span><span class="Comment">//: Only supports elements immediately inside containers; no arrays or</span>
<span id="L144" class="LineNr">144 </span><span class="Comment">//: containers within containers yet.</span>
<span id="L145" class="LineNr">145 </span>
<span id="L146" class="LineNr">146 </span><span class="Delimiter">:(scenario get_location)</span>
<span id="L147" class="LineNr">147 </span><span class="muRecipe">def</span> <a href='000organization.cc.html#L113'>main</a> [
<span id="L148" class="LineNr">148 </span> <span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span id="L149" class="LineNr">149 </span> <span class="Constant">13</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span id="L150" class="LineNr">150 </span> <span class="Constant">15</span>:location<span class="Special"> <- </span>get-location <span class="Constant">12</span>:point<span class="Delimiter">,</span> <span class="Constant">1:offset</span>
<span id="L151" class="LineNr">151 </span>]
<span id="L152" class="LineNr">152 </span><span class="traceContains">+mem: storing 13 in location 15</span>
<span id="L153" class="LineNr">153 </span>
<span id="L154" class="LineNr">154 </span><span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
<span id="L155" class="LineNr">155 </span>GET_LOCATION<span class="Delimiter">,</span>
<span id="L156" class="LineNr">156 </span><span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
<span id="L157" class="LineNr">157 </span><a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"get-location"</span><span class="Delimiter">,</span> GET_LOCATION<span class="Delimiter">);</span>
<span id="L158" class="LineNr">158 </span><span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span id="L159" class="LineNr">159 </span><span class="Normal">case</span> GET_LOCATION: <span class="Delimiter">{</span>
<span id="L160" class="LineNr">160 </span> <span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L161" class="LineNr">161 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'get-location' expects exactly 2 ingredients in '"</span> << to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L162" class="LineNr">162 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L163" class="LineNr">163 </span> <span class="Delimiter">}</span>
<span id="L164" class="LineNr">164 </span> reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> base = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span id="L165" class="LineNr">165 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!canonize_type<span class="Delimiter">(</span>base<span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L166" class="LineNr">166 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!base<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L167" class="LineNr">167 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'get-location' should be a container, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L168" class="LineNr">168 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L169" class="LineNr">169 </span> <span class="Delimiter">}</span>
<span id="L170" class="LineNr">170 </span> <span class="Normal">const</span> type_tree* base_root_type = base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>atom ? base<span class="Delimiter">.</span>type : base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>left<span class="Delimiter">;</span>
<span id="L171" class="LineNr">171 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!base_root_type<span class="Delimiter">-></span>atom || base_root_type<span class="Delimiter">-></span>value == <span class="Constant">0</span> || !contains_key<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_root_type<span class="Delimiter">-></span>value<span class="Delimiter">)</span> || get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_root_type<span class="Delimiter">-></span>value<span class="Delimiter">).</span>kind != <a href='010vm.cc.html#L173'>CONTAINER</a><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L172" class="LineNr">172 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'get-location' should be a container, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L173" class="LineNr">173 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L174" class="LineNr">174 </span> <span class="Delimiter">}</span>
<span id="L175" class="LineNr">175 </span> <a href='010vm.cc.html#L123'>type_ordinal</a> base_type = base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>value<span class="Delimiter">;</span>
<span id="L176" class="LineNr">176 </span> <span class="Normal">const</span> reagent& offset = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
<span id="L177" class="LineNr">177 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!is_literal<span class="Delimiter">(</span>offset<span class="Delimiter">)</span> || !is_mu_scalar<span class="Delimiter">(</span>offset<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L178" class="LineNr">178 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"second ingredient of 'get-location' should have type 'offset', but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L179" class="LineNr">179 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L180" class="LineNr">180 </span> <span class="Delimiter">}</span>
<span id="L181" class="LineNr">181 </span> <span class="Normal">int</span> offset_value = <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L182" class="LineNr">182 </span> <span class="Comment">//: later layers will permit non-integer offsets</span>
<span id="L183" class="LineNr">183 </span> <span class="Normal">if</span> <span class="Delimiter">(</span><a href='002test.cc.html#L85'>is_integer</a><span class="Delimiter">(</span>offset<span class="Delimiter">.</span>name<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L184" class="LineNr">184 </span> <span class="Conceal">¦</span> offset_value = <a href='002test.cc.html#L91'>to_integer</a><span class="Delimiter">(</span>offset<span class="Delimiter">.</span>name<span class="Delimiter">);</span>
<span id="L185" class="LineNr">185 </span> <span class="Conceal">¦</span> <span class="Normal">if</span> <span class="Delimiter">(</span>offset_value < <span class="Constant">0</span> || offset_value >= <a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_type<span class="Delimiter">).</span>elements<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L186" class="LineNr">186 </span> <span class="Conceal">¦</span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"invalid offset "</span> << offset_value << <span class="Constant">" for '"</span> << get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_type<span class="Delimiter">).</span>name << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L187" class="LineNr">187 </span> <span class="Conceal">¦</span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L188" class="LineNr">188 </span> <span class="Conceal">¦</span> <span class="Delimiter">}</span>
<span id="L189" class="LineNr">189 </span> <span class="Delimiter">}</span>
<span id="L190" class="LineNr">190 </span> <span class="Normal">else</span> <span class="Delimiter">{</span>
<span id="L191" class="LineNr">191 </span> <span class="Conceal">¦</span> offset_value = offset<span class="Delimiter">.</span>value<span class="Delimiter">;</span>
<span id="L192" class="LineNr">192 </span> <span class="Delimiter">}</span>
<span id="L193" class="LineNr">193 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L194" class="LineNr">194 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
<span id="L195" class="LineNr">195 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'get-location "</span> << base<span class="Delimiter">.</span>original_string << <span class="Constant">", "</span> << offset<span class="Delimiter">.</span>original_string << <span class="Constant">"' should write to type location but '"</span> << inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name << <span class="Constant">"' has type '"</span> << names_to_string_without_quotes<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>type<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L196" class="LineNr">196 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L197" class="LineNr">197 </span> <span class="Delimiter">}</span>
<span id="L198" class="LineNr">198 </span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L199" class="LineNr">199 </span><span class="Delimiter">}</span>
<span id="L200" class="LineNr">200 </span><span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span id="L201" class="LineNr">201 </span><span class="Normal">case</span> GET_LOCATION: <span class="Delimiter">{</span>
<span id="L202" class="LineNr">202 </span> reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> base = <a href='026call.cc.html#L85'>current_instruction</a><span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span id="L203" class="LineNr">203 </span> canonize<span class="Delimiter">(</span>base<span class="Delimiter">);</span>
<span id="L204" class="LineNr">204 </span> <span class="Normal">int</span> base_address = base<span class="Delimiter">.</span>value<span class="Delimiter">;</span>
<span id="L205" class="LineNr">205 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>base_address == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L206" class="LineNr">206 </span> <span class="Conceal">¦</span> <a href='003trace.cc.html#L168'>raise</a> << <a href='013update_operation.cc.html#L25'>maybe</a><span class="Delimiter">(</span><a href='026call.cc.html#L83'>current_recipe_name</a><span class="Delimiter">())</span> << <span class="Constant">"tried to access location 0 in '"</span> << to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << <a href='003trace.cc.html#L197'>end</a><span class="Delimiter">();</span>
<span id="L207" class="LineNr">207 </span> <span class="Conceal">¦</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L208" class="LineNr">208 </span> <span class="Delimiter">}</span>
<span id="L209" class="LineNr">209 </span> <span class="Normal">const</span> type_tree* base_type = get_base_type<span class="Delimiter">(</span>base<span class="Delimiter">.</span>type<span class="Delimiter">);</span>
<span id="L210" class="LineNr">210 </span> <span class="Normal">int</span> offset = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span id="L211" class="LineNr">211 </span> <span class="Normal">if</span> <span class="Delimiter">(</span>offset < <span class="Constant">0</span> || offset >= <a href='001help.cc.html#L141'>SIZE</a><span class="Delimiter">(</span>get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_type<span class="Delimiter">-></span>value<span class="Delimiter">).</span>elements<span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span> <span class="Comment">// copied from Check above</span>
<span id="L212" class="LineNr">212 </span> <span class="Normal">int</span> result = base_address<span class="Delimiter">;</span>
<span id="L213" class="LineNr">213 </span> <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < offset<span class="Delimiter">;</span> ++i<span class="Delimiter">)</span>
<span id="L214" class="LineNr">214 </span> <span class="Conceal">¦</span> result += size_of<span class="Delimiter">(</span><a href='030container.cc.html#L429'>element_type</a><span class="Delimiter"pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */# The Mu computer's level-2 language, also called Mu.
# http://akkartik.name/post/mu-2019-2
#
# To run:
# $ ./translate_subx init.linux [0-9]*.subx apps/mu.subx
# == Goals
# 1. Be memory safe. It should be impossible to corrupt the heap, or to create
# a bad pointer. (Requires strong type safety.)
# 2. Do as little as possible to achieve goal 1. The translator should be
# implementable in machine code.
# - minimize impedance mismatch between source language and SubX target
# (e.g. programmer manages registers manually)
# - checks over syntax
# (e.g. programmer's register allocation is checked)
# - runtime checks to avoid complex static analysis
# (e.g. array indexing always checks bounds)
# == Language description
# A program is a sequence of function definitions.
#
# Function example:
# fn foo n: int -> result/eax: int {
# ...
# }
#
# Functions consist of a name, optional inputs, optional outputs and a block.
#
# Function inputs and outputs are variables. All variables have a type and
# storage specifier. They can be placed either in memory (on the stack) or in
# one of 6 named registers.
# eax ecx edx ebx esi edi
# Variables in registers must be primitive 32-bit types.
# Variables not explicitly placed in a register are on the stack.
#
# Function inputs are always passed in memory (on the stack), while outputs
# are always returned in registers.
#
# Blocks mostly consist of statements.
#
# Statements mostly consist of a name, optional inputs and optional outputs.
#
# Statement inputs are variables or literals. Variables need to specify type
# (and storage) the first time they're mentioned but not later.
#
# Statement outputs, like function outputs, must be variables in registers.
#
# Statement names must be either primitives or user-defined functions.
#
# Primitives can write to any register.
# User-defined functions only write to hard-coded registers. Outputs of each
# call must have the same registers as in the function definition.
#
# There are some other statement types:
# - blocks. Multiple statements surrounded by '{...}' and optionally
# prefixed with a label name and ':'
# - {
# ...
# }
# - foo: {
# ...
# }
#
# - variable definitions on the stack. E.g.:
# - var foo: int
# - var bar: (array int 3)
# There's no initializer; variables are automatically initialized.
# The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
#
# - variables definitions in a register. E.g.:
# - var foo/eax: int <- add bar 1
# The initializer is mandatory and must be a valid instruction that writes
# a single output to the right register. In practice registers will
# usually be either initialized by primitives or copied from eax.
# - var eax: int <- foo bar quux
# var floo/ecx: int <- copy eax
#
# Still todo:
# global variables
# heap allocations (planned name: 'handle')
# user-defined types: 'type' for structs, 'choice' for unions
# short-lived 'address' type for efficiently writing inside nested structs
#
# We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
# and 'address' in comments. Their definitions are in layer 50, but really you
# can ignore the distinctions on a first reading of this program.
#
# Formal types:
# A program is a linked list of functions
# A function contains:
# name: (handle array byte)
# inouts: linked list of vars <-- 'inouts' is more precise than 'inputs'
# data: (handle var)
# next: (handle list)
# outputs: linked list of vars
# data: (handle var)
# next: (handle list)
# body: (handle block)
# A var-type contains:
# name: (handle array byte)
# type: (handle tree type-id)
#
# A statement can be:
# tag 0: a block
# tag 1: a simple statement (stmt1)
# tag 2: a variable defined on the stack
# tag 3: a variable defined in a register
#
# A block contains:
# tag: 0
# statements: (handle list stmt)
# name: (handle array byte) -- starting with '$'
#
# A regular statement contains:
# tag: 1
# operation: (handle array byte)
# inouts: (handle list operand)
# outputs: (handle list var)
#
# A variable defined on the stack contains:
# tag: 2
# name: (handle array byte)
# type: (handle tree type-id)
#
# A variable defined in a register contains:
# tag: 3
# name: (handle array byte)
# type: (handle tree type-id)
# reg: (handle array byte)
# == Translation: managing the stack
# Now that we know what the language looks like in the large, let's think
# about how translation happens from the bottom up. One crucial piece of the
# puzzle is how Mu will clean up variables defined on the stack for you.
#
# Assume that we maintain a 'functions' list while parsing source code. And a
# 'primitives' list is a global constant. Both these contain enough information
# to perform type-checking on function calls or primitive statements, respectively.
#
# Defining variables pushes them on a stack with the current block depth and
# enough information about their location (stack offset or register).
# Starting a block increments the current block id.
# Each statement now has enough information to emit code for it.
# Ending a block is where the magic happens:
# pop all variables at the current block depth
# emit code to restore all register variables introduced at the current depth
# emit code to clean up all stack variables at the current depth (just increment esp)
# decrement the current block depth
#
# Formal types:
# live-vars: stack of vars
# var:
# name: (handle array byte)
# type: (handle tree type-id)
# block: int
# stack-offset: int (added to ebp)
# register: (handle array byte)
# either usual register names
# or '*' to indicate any register
# At most one of stack-offset or register-index must be non-zero.
# A register of '*' designates a variable _template_. Only legal in formal
# parameters for primitives.
# == Translating a single function call
# This one's easy. Assuming we've already checked things, we just drop the
# outputs (which use hard-coded registers) and emit inputs in a standard format.
#
# out1, out2, out3, ... <- name inout1, inout2, inout3, ...
# =>
# (subx-name inout1 inout2 inout3)
#
# Formal types:
# functions: linked list of info
# name: (handle array byte)
# inouts: linked list of vars
# outputs: linked list of vars
# body: block (singleton linked list)
# subx-name: (handle array byte)
# == Translating a single primitive instruction
# A second crucial piece of the puzzle is how Mu converts fairly regular
# primitives with their uniform syntax to SubX instructions with their gnarly
# x86 details.
#
# Mu instructions have inputs and outputs. Primitives can have up to 2 of
# them.
# SubX instructions have rm32 and r32 operands.
# The translation between them covers almost all the possibilities.
# Instructions with 1 inout may turn into ones with 1 rm32
# (e.g. incrementing a var on the stack)
# Instructions with 1 output may turn into ones with 1 rm32
# (e.g. incrementing a var in a register)
# 1 inout and 1 output may turn into 1 rm32 and 1 r32
# (e.g. adding a var to a reg)
# 2 inouts may turn into 1 rm32 and 1 r32
# (e.g. adding a reg to a var)
# 1 inout and 1 literal may turn into 1 rm32 and 1 imm32
# (e.g. adding a constant to a var)
# 1 output and 1 literal may turn into 1 rm32 and 1 imm32
# (e.g. adding a constant to a reg)
# 2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
# (special-case: divide edx:eax by a var or reg)
# Observations:
# We always emit rm32. It may be the first inout or the first output.
# We may emit r32 or imm32 or neither.
# When we emit r32 it may come from first inout or second inout or first output.
#
# Accordingly, the formal data structure for a primitive looks like this:
# primitives: linked list of info
# name: (handle array byte)
# mu-inouts: linked list of vars to check
# mu-outputs: linked list of vars to check; at most a singleton
# subx-name: (handle array byte)
# subx-rm32: enum arg-location
# subx-r32: enum arg-location
# subx-imm32: enum arg-location
# subx-disp32: enum arg-location
# output-is-write-only: boolean
# arg-location: enum
# 0 means none
# 1 means first inout
# 2 means second inout
# 3 means first output
# == Translating a block
# Emit block name if necessary
# Emit '{'
# When you encounter a statement, emit it as above
# When you encounter a variable declaration
# emit any code needed for it (bzeros)
# push it on the var stack
# update register dict if necessary
# When you encounter '}'
# While popping variables off the var stack until block id changes
# Emit code needed to clean up the stack
# either increment esp
# or pop into appropriate register
# The rest is straightforward.
== data
Program:
_Program-functions: # (handle function)
0/imm32
_Program-types: # (handle typeinfo)
0/imm32
# Some constants for simulating the data structures described above.
# Many constants here come with a type in a comment.
#
# Sometimes the type is of the value at that offset for the given type. For
# example, if you start at a function record and move forward Function-inouts
# bytes, you'll find a (handle list var).
#
# At other times, the type is of the constant itself. For example, the type of
# the constant Function-size is (addr int). To get the size of a function,
# look in *Function-size.
Function-name: # (handle array byte)
0/imm32
Function-subx-name: # (handle array byte)
4/imm32
Function-inouts: # (handle list var)
8/imm32
Function-outputs: # (handle list var)
0xc/imm32
Function-body: # (handle block)
0x10/imm32
Function-next: # (handle function)
0x14/imm32
Function-size: # (addr int)
0x18/imm32/24
Primitive-name: # (handle array byte)
0/imm32
Primitive-inouts: # (handle list var)
4/imm32
Primitive-outputs: # (handle list var)
8/imm32
Primitive-subx-name: # (handle array byte)
0xc/imm32
Primitive-subx-rm32: # enum arg-location
0x10/imm32
Primitive-subx-r32: # enum arg-location
0x14/imm32
Primitive-subx-imm32: # enum arg-location
0x18/imm32
Primitive-subx-disp32: # enum arg-location -- only for branches
0x1c/imm32
Primitive-output-is-write-only: # boolean
0x20/imm32
Primitive-next: # (handle function)
0x24/imm32
Primitive-size: # (addr int)
0x28/imm32/36
Stmt-tag: # int
0/imm32
Block-stmts: # (handle list stmt)
4/imm32
Block-var: # (handle var)
8/imm32
Stmt1-operation: # (handle array byte)
4/imm32
Stmt1-inouts: # (handle stmt-var)
8/imm32
Stmt1-outputs: # (handle stmt-var)
0xc/imm32
Vardef-var: # (handle var)
4/imm32
Regvardef-operation: # (handle array byte)
4/imm32
Regvardef-inouts: # (handle stmt-var)
8/imm32
Regvardef-outputs: # (handle stmt-var) # will have exactly one element
0xc/imm32
Stmt-size: # (addr int)
0x10/imm32
Var-name: # (handle array byte)
0/imm32
Var-type: # (handle tree type-id)
4/imm32
Var-block-depth: # int
8/imm32
Var-offset: # int
0xc/imm32
Var-register: # (handle array byte) -- name of a register
0x10/imm32
Var-size: # (addr int)
0x14/imm32
Any-register: # wildcard
# size
1/imm32
# data
2a/asterisk
List-value:
0/imm32
List-next: # (handle list _)
4/imm32
List-size: # (addr int)
8/imm32
# A stmt-var is like a list of vars with call-site specific metadata
Stmt-var-value: # (handle var)
0/imm32
Stmt-var-next: # (handle stmt-var)
4/imm32
Stmt-var-is-deref: # boolean
8/imm32
Stmt-var-size: # (addr int)
0xc/imm32
# Types are expressed as trees (s-expressions) of type-ids (ints).
# However, there's no need for singletons, so we can assume (int) == int
# - if x->right == nil, x is an atom
# - x->left contains either a pointer to a pair, or an atomic type-id directly.
# type ids will be less than 0x10000 (MAX_TYPE_ID).
Tree-left: # either type-id or (addr tree type-id)
0/imm32
Tree-right: # (addr tree type-id)
4/imm32
Tree-size: # (addr int)
8/imm32
# Types
Max-type-id:
0x10000/imm32
Type-id: # (stream (address array byte))
0x1c/imm32/write
0/imm32/read
0x100/imm32/length
# data
"literal"/imm32 # 0
"int"/imm32 # 1
"addr"/imm32 # 2
"array"/imm32 # 3
"handle"/imm32 # 4
"boolean"/imm32 # 5
"constant"/imm32 # 6: like a literal, but replaced with its value in Var-offset
0/imm32
# 0x20
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
# Program->types contains some typeinfo for each type definition.
# Types contain vars with types, but can't specify registers.
Typeinfo-id: # type-id
0/imm32
# each field of a type is represented using two var's:
# 1. the input var: expected type of the field; convenient for creating using parse-var-with-type
# 2. the output var: a constant containing the byte offset; convenient for code-generation
Typeinfo-fields: # (handle table string {(handle var), (handle var)})
4/imm32
Typeinfo-next: # (handle typeinfo)
8/imm32
Typeinfo-size: # (addr int)
0xc/imm32
== code
Entry:
# . prologue
89/<- %ebp 4/r32/esp
(new-segment *Heap-size Heap)
# if (argv[1] == "test') run-tests()
{
# if (argc <= 1) break
81 7/subop/compare *ebp 1/imm32
7e/jump-if-<= break/disp8
# if (argv[1] != "test") break
(kernel-string-equal? *(ebp+8) "test") # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
#
(run-tests)
# syscall(exit, *Num-test-failures)
8b/-> *Num-test-failures 3/r32/ebx
eb/jump $mu-main:end/disp8
}
# otherwise convert Stdin
(convert-mu Stdin Stdout)
(flush Stdout)
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$mu-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
convert-mu: # in: (addr buffered-file), out: (addr buffered-file)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(parse-mu *(ebp+8))
(check-mu-types)
(emit-subx *(ebp+0xc))
$convert-mu:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-empty-input:
# empty input => empty output
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
#
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
(check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-skeleton:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-skeleton/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-skeleton/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-skeleton/4")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-skeleton/6")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-skeleton/7")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-multiple-function-skeletons:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream "}\n")
(write _test-input-stream "fn bar {\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check first function
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-multiple-function-skeletons/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-multiple-function-skeletons/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-multiple-function-skeletons/4")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-multiple-function-skeletons/7")
# check second function
(check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-multiple-function-skeletons/11")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-multiple-function-skeletons/12")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-multiple-function-skeletons/14")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-multiple-function-skeletons/17")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-arg:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
#
(write _test-input-stream "fn foo n: int {\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-arg/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-arg/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-arg/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-arg/3")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-arg/4")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-arg/5")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-arg/6")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-arg/7")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-arg-and-body:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo n: int {\n")
(write _test-input-stream " increment n\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-arg-and-body/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-arg-and-body/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-arg-and-body/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-arg-and-body/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-arg-and-body/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-arg-and-body/5")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-arg-and-body/6")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-arg-and-body/7")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-arg-and-body/8")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-arg-and-body/9")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-arg-and-body/10")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-arg-and-body/11")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-arg-and-body/12")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-distinguishes-args:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: int, b: int {\n")
(write _test-input-stream " increment b\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-distinguishes-args/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-distinguishes-args/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-distinguishes-args/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-distinguishes-args/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-distinguishes-args/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-distinguishes-args/5")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x0000000c)" "F - test-convert-function-distinguishes-args/6")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-distinguishes-args/7")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-distinguishes-args/8")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-distinguishes-args/9")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-distinguishes-args/10")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-distinguishes-args/11")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-distinguishes-args/12")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-returns-result:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
(write _test-input-stream " result <- copy a\n")
(write _test-input-stream " result <- increment\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-returns-result/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-returns-result/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-returns-result/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-returns-result/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-returns-result/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-returns-result/5")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-function-returns-result/6")
(check-next-stream-line-equal _test-output-stream " 40/increment-eax" "F - test-convert-function-returns-result/7")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-returns-result/8")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-returns-result/9")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-returns-result/10")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-returns-result/11")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-returns-result/12")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-returns-result/13")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-literal-arg:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
(write _test-input-stream " result <- copy a\n")
(write _test-input-stream " result <- add 1\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-literal-arg/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-literal-arg/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-literal-arg/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-literal-arg/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-literal-arg/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-literal-arg/5")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-function-literal-arg/6")
(check-next-stream-line-equal _test-output-stream " 05/add-to-eax 1/imm32" "F - test-convert-function-literal-arg/7")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-literal-arg/8")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-literal-arg/9")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-literal-arg/10")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-literal-arg/11")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-literal-arg/12")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-literal-arg/13")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-literal-arg-2:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
(write _test-input-stream " result <- copy a\n")
(write _test-input-stream " result <- add 1\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-literal-arg-2/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-literal-arg-2/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-literal-arg-2/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-literal-arg-2/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-literal-arg-2/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-literal-arg-2/5")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000003/r32" "F - test-convert-function-literal-arg-2/6")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %ebx 1/imm32" "F - test-convert-function-literal-arg-2/7")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-literal-arg-2/8")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-literal-arg-2/9")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-literal-arg-2/10")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-literal-arg-2/11")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-literal-arg-2/12")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-literal-arg-2/13")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-call-with-literal-arg:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn main -> result/ebx: int {\n")
(write _test-input-stream " result <- do-add 3 4\n")
(write _test-input-stream "}\n")
(write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
(write _test-input-stream " result <- copy a\n")
(write _test-input-stream " result <- add b\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "main:" "F - test-convert-function-call-with-literal-arg/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-call-with-literal-arg/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-call-with-literal-arg/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-call-with-literal-arg/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-call-with-literal-arg/4")
(check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:" "F - test-convert-function-call-with-literal-arg/5")
(check-next-stream-line-equal _test-output-stream " (do-add 3 4)" "F - test-convert-function-call-with-literal-arg/6")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-call-with-literal-arg/7")
(check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-call-with-literal-arg/9")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-call-with-literal-arg/10")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-call-with-literal-arg/11")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-call-with-literal-arg/12")
(check-next-stream-line-equal _test-output-stream "do-add:" "F - test-convert-function-call-with-literal-arg/13")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-call-with-literal-arg/14")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-call-with-literal-arg/15")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-call-with-literal-arg/16")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-call-with-literal-arg/17")
(check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:" "F - test-convert-function-call-with-literal-arg/18")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/19")
(check-next-stream-line-equal _test-output-stream " 03/add *(ebp+0x0000000c) 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/20")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-call-with-literal-arg/21")
(check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:" "F - test-convert-function-call-with-literal-arg/22")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-call-with-literal-arg/23")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-call-with-literal-arg/24")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-call-with-literal-arg/25")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-call-with-literal-arg/26")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-local-var-in-mem:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-mem/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-mem/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-mem/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-mem/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-mem/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-mem/5")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-mem/6")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-mem/7")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-mem/8")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-mem/9")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-mem/10")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-mem/11")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-mem/12")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-mem/13")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-mem/14")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-local-var-in-reg:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " var x/ecx: int <- copy 3\n")
(write _test-input-stream " x <- increment\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-reg/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-reg/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-reg/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-reg/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-reg/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-reg/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-with-local-var-in-reg/6")
(check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-convert-function-with-local-var-in-reg/7")
(check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-convert-function-with-local-var-in-reg/8")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-reg/10")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-reg/11")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-reg/12")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-reg/13")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-reg/14")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-reg/15")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-local-var-dereferenced:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " var x/ecx: (addr int) <- copy 0\n")
(write _test-input-stream " increment *x\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-dereferenced/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-dereferenced/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-dereferenced/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-dereferenced/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-dereferenced/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-dereferenced/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-with-local-var-dereferenced/6")
(check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 0/imm32" "F - test-convert-function-with-local-var-dereferenced/7")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *ecx" "F - test-convert-function-with-local-var-dereferenced/8")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-dereferenced/10")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-dereferenced/11")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-dereferenced/12")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-dereferenced/13")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-dereferenced/14")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-dereferenced/15")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-compare-register-with-literal:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " var x/ecx: int <- copy 0\n")
(write _test-input-stream " compare x, 0\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-compare-register-with-literal/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-compare-register-with-literal/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-compare-register-with-literal/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-compare-register-with-literal/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-compare-register-with-literal/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-compare-register-with-literal/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-compare-register-with-literal/6")
(check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 0/imm32" "F - test-convert-compare-register-with-literal/7")
(check-next-stream-line-equal _test-output-stream " 81 7/subop/compare %ecx 0/imm32" "F - test-convert-compare-register-with-literal/8")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-compare-register-with-literal/10")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-compare-register-with-literal/11")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-compare-register-with-literal/12")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-compare-register-with-literal/13")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-compare-register-with-literal/14")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-compare-register-with-literal/15")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-local-var-in-block:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-block/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-block/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-block/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-block/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-block/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-block/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-block/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-local-var-in-block/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-block/8")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-block/9")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-block/10")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-block/11")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-local-var-in-block/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-block/13")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-block/14")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-block/15")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-block/16")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-block/17")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-block/18")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-local-var-in-named-block:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " $bar: {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-named-block/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-named-block/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-named-block/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-named-block/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-named-block/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-named-block/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-named-block/6")
(check-next-stream-line-equal _test-output-stream "$bar:loop:" "F - test-convert-function-with-local-var-in-named-block/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-named-block/8")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-named-block/9")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-named-block/10")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-named-block/11")
(check-next-stream-line-equal _test-output-stream "$bar:break:" "F - test-convert-function-with-local-var-in-named-block/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-named-block/13")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-named-block/14")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-named-block/15")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-named-block/16")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-named-block/17")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-named-block/18")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-branches-in-block:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo x: int {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " break-if->=\n")
(write _test-input-stream " loop-if-addr<\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " loop\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-in-block/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-in-block/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-in-block/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-in-block/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-block/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-in-block/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-block/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-in-block/7")
(check-next-stream-line-equal _test-output-stream " 0f 8d/jump-if->= break/disp32" "F - test-convert-function-with-branches-in-block/8")
(check-next-stream-line-equal _test-output-stream " 0f 82/jump-if-addr< loop/disp32" "F - test-convert-function-with-branches-in-block/9")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-branches-in-block/10")
(check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-in-block/11")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-block/12")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-in-block/13")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-block/14")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-in-block/15")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-in-block/16")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-in-block/17")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-in-block/18")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-in-block/19")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-branches-in-named-block:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo x: int {\n")
(write _test-input-stream " $bar: {\n")
(write _test-input-stream " break-if->= $bar\n")
(write _test-input-stream " loop-if-addr< $bar\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " loop\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-in-named-block/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-in-named-block/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-in-named-block/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-in-named-block/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-named-block/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-in-named-block/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-named-block/6")
(check-next-stream-line-equal _test-output-stream "$bar:loop:" "F - test-convert-function-with-branches-in-named-block/7")
(check-next-stream-line-equal _test-output-stream " 0f 8d/jump-if->= $bar:break/disp32" "F - test-convert-function-with-branches-in-named-block/8")
(check-next-stream-line-equal _test-output-stream " 0f 82/jump-if-addr< $bar:loop/disp32" "F - test-convert-function-with-branches-in-named-block/9")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-branches-in-named-block/10")
(check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-in-named-block/11")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-named-block/12")
(check-next-stream-line-equal _test-output-stream "$bar:break:" "F - test-convert-function-with-branches-in-named-block/13")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-named-block/14")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-in-named-block/15")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-in-named-block/16")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-in-named-block/17")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-in-named-block/18")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-in-named-block/19")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-var-in-nested-block:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo x: int {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-var-in-nested-block/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-var-in-nested-block/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-var-in-nested-block/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-var-in-nested-block/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-var-in-nested-block/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-var-in-nested-block/7")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/8")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-var-in-nested-block/9")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-var-in-nested-block/11")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-var-in-nested-block/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/13")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-var-in-nested-block/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-var-in-nested-block/16")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/17")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-var-in-nested-block/18")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-var-in-nested-block/19")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-var-in-nested-block/20")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-var-in-nested-block/21")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-var-in-nested-block/22")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-multiple-vars-in-nested-blocks:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo x: int {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x/eax: int <- copy 0\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var y: int\n")
(write _test-input-stream " x <- add y\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
(check-next-stream-line-equal _test-output-stream " b8/copy-to-eax 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/9")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
(check-next-stream-line-equal _test-output-stream " 03/add *(ebp+0xfffffff8) 0x00000000/r32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/13")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-branches-and-local-vars:
# A conditional 'break' after a 'var' in a block is converted into a
# nested block that performs all necessary cleanup before jumping. This
# results in some ugly code duplication.
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " break-if->=\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-branches-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-branches-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:break/disp32" "F - test-convert-function-with-branches-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-branches-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-and-local-vars/23")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-conditional-loops-and-local-vars:
# A conditional 'loop' after a 'var' in a block is converted into a nested
# block that performs all necessary cleanup before jumping. This results
# in some ugly code duplication.
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " loop-if->=\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-conditional-loops-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-conditional-loops-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-conditional-loops-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-conditional-loops-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-conditional-loops-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-conditional-loops-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:loop/disp32" "F - test-convert-function-with-conditional-loops-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-conditional-loops-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-conditional-loops-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-conditional-loops-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-conditional-loops-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-conditional-loops-and-local-vars/23")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-unconditional-loops-and-local-vars:
# An unconditional 'loop' after a 'var' in a block is emitted _after_ the
# regular block cleanup. Any instructions after 'loop' are dead and
# therefore skipped.
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " loop\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-loops-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
# not emitted: ff 0/subop/increment *(ebp+0xfffffffc)
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-branches-and-loops-and-local-vars:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " break-if->=\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " loop\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-and-loops-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:break/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-branches-and-loops-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " a: {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var y: int\n")
(write _test-input-stream " break-if->= a\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " loop\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " e9/jump a:break/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
(check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/29")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/30")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " a: {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var y: int\n")
(write _test-input-stream " break a\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " e9/jump a:break/disp32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-unconditional-break-and-local-vars:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var y: int\n")
(write _test-input-stream " break\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-unconditional-break-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-unconditional-break-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-unconditional-break-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-unconditional-break-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-unconditional-break-and-local-vars/23")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " a: {\n")
(write _test-input-stream " var x: int\n")
(write _test-input-stream " {\n")
(write _test-input-stream " var y: int\n")
(write _test-input-stream " loop a\n")
(write _test-input-stream " increment x\n")
(write _test-input-stream " }\n")
(write _test-input-stream " }\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
(check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
(check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13")
(check-next-stream-line-equal _test-output-stream " e9/jump a:loop/disp32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
(check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
(check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-length-of-array:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: (addr array int) {\n")
(write _test-input-stream " var b/eax: (addr array int) <- copy a\n")
(write _test-input-stream " var c/eax: int <- length b\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-length-of-array/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-length-of-array/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-length-of-array/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-length-of-array/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-length-of-array/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-length-of-array/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/6")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-length-of-array/7")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/8")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *eax 0x00000000/r32" "F - test-convert-length-of-array/9")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/10")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/11")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array/12")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array/13")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array/14")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array/15")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array/16")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array/17")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-index-into-array:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo {\n")
(write _test-input-stream " var arr/eax: (addr array int) <- copy 0\n")
(write _test-input-stream " var idx/ecx: int <- copy 3\n")
(write _test-input-stream " var x/eax: (addr int) <- index arr, idx\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-length-of-array/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-length-of-array/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-length-of-array/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-length-of-array/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-length-of-array/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-length-of-array/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/6")
(check-next-stream-line-equal _test-output-stream " b8/copy-to-eax 0/imm32" "F - test-convert-length-of-array/7")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-length-of-array/8")
(check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-convert-length-of-array/9")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/10")
(check-next-stream-line-equal _test-output-stream " 8d/copy-address *(eax + ecx<<2 + 4) 0x00000000/r32" "F - test-convert-length-of-array/11")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/12")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-length-of-array/13")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array/16")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array/17")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array/18")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array/19")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array/20")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-convert-function-and-type-definition:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(clear-stream $_test-input-buffered-file->buffer)
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
c7 0/subop/copy *Next-block-index 1/imm32
#
(write _test-input-stream "fn foo a: (addr t) {\n")
(write _test-input-stream " var _a/eax: (addr t) <- copy a\n")
(write _test-input-stream " var b/ecx: (addr int) <- get _a, x\n")
(write _test-input-stream " var c/ecx: (addr int) <- get _a, y\n")
(write _test-input-stream "}\n")
(write _test-input-stream "type t {\n")
(write _test-input-stream " x: int\n")
(write _test-input-stream " y: int\n")
(write _test-input-stream "}\n")
# convert
(convert-mu _test-input-buffered-file _test-output-buffered-file)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-and-type-definition/0")
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-and-type-definition/1")
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-and-type-definition/2")
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-and-type-definition/3")
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-and-type-definition/4")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-and-type-definition/5")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-function-and-type-definition/6")
(check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-function-and-type-definition/7")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-and-type-definition/8")
(check-next-stream-line-equal _test-output-stream " 8d/copy-address *(eax + 0x00000000) 0x00000001/r32" "F - test-convert-function-and-type-definition/9")
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-and-type-definition/10")
(check-next-stream-line-equal _test-output-stream " 8d/copy-address *(eax + 0x00000004) 0x00000001/r32" "F - test-convert-function-and-type-definition/11")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/12")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-and-type-definition/15")
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-and-type-definition/16")
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-and-type-definition/17")
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-and-type-definition/18")
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-and-type-definition/19")
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-and-type-definition/20")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
#######################################################
# Parsing
#######################################################
parse-mu: # in: (addr buffered-file)
# pseudocode
# var curr-function: (addr (handle function)) = Program->functions
# var curr-type: (addr (handle typeinfo)) = Program->types
# var line: (stream byte 512)
# var word-slice: slice
# while true # line loop
# clear-stream(line)
# read-line-buffered(in, line)
# if (line->write == 0) break # end of file
# word-slice = next-mu-token(line)
# if slice-empty?(word-slice) # end of line
# continue
# else if slice-starts-with?(word-slice, "#") # comment
# continue # end of line
# else if slice-equal?(word-slice, "fn")
# var new-function: (handle function) = allocate(function)
# var vars: (stack (addr var) 256)
# populate-mu-function-header(in, new-function, vars)
# populate-mu-function-body(in, new-function, vars)
# assert(vars->top == 0)
# *curr-function = new-function
# curr-function = &new-function->next
# else if slice-equal?(word-slice, "type")
# word-slice = next-mu-token(line)
# type-id = pos-or-insert-slice(Type-id, word-slice)
# var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
# assert(next-word(line) == "{")
# populate-mu-type(in, new-type)
# else
# abort()
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# var line/ecx: (stream byte 512)
81 5/subop/subtract %esp 0x200/imm32
68/push 0x200/imm32/length
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %ecx 4/r32/esp
# var word-slice/edx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %edx 4/r32/esp
# var curr-function/edi: (addr (handle function))
bf/copy-to-edi _Program-functions/imm32
# var curr-type/esi: (addr (handle typeinfo))
be/copy-to-esi _Program-types/imm32
# var vars/ebx: (stack (addr var) 256)
81 5/subop/subtract %esp 0x400/imm32
68/push 0x400/imm32/length
68/push 0/imm32/top
89/<- %ebx 4/r32/esp
{
$parse-mu:line-loop:
(clear-stream %ecx)
(read-line-buffered *(ebp+8) %ecx)
# if (line->write == 0) break
81 7/subop/compare *ecx 0/imm32
0f 84/jump-if-= break/disp32
#? # dump line {{{
#? (write 2 "parse-mu: ^")
#? (write-stream 2 %ecx)
#? (write 2 "$\n")
#? (rewind-stream %ecx)
#? # }}}
(next-mu-token %ecx %edx)
# if slice-empty?(word-slice) continue
(slice-empty? %edx)
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= loop/disp32
# if (*word-slice->start == "#") continue
# . eax = *word-slice->start
8b/-> *edx 0/r32/eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# . if (eax == '#') continue
3d/compare-eax-and 0x23/imm32/hash
0f 84/jump-if-= loop/disp32
# if (slice-equal?(word-slice, "fn")) parse a function
{
$parse-mu:fn:
(slice-equal? %edx "fn")
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
# var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
(allocate Heap *Function-size) # => eax
(zero-out %eax *Function-size)
(clear-stack %ebx)
(populate-mu-function-header %ecx %eax %ebx)
(populate-mu-function-body *(ebp+8) %eax %ebx)
# *curr-function = new-function
89/<- *edi 0/r32/eax
# curr-function = &new-function->next
8d/address-> *(eax+0x14) 7/r32/edi # Function-next
e9/jump $parse-mu:line-loop/disp32
}
# if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
{
$parse-mu:type:
(slice-equal? %edx "type")
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
(next-mu-token %ecx %edx)
# var type-id/eax: int
(pos-or-insert-slice Type-id %edx) # => eax
# var new-type/eax: (handle typeinfo)
(find-or-create-typeinfo %eax) # => eax
# TODO: ensure that 'line' has nothing else but '{'
(populate-mu-type *(ebp+8) %eax) # => eax
e9/jump $parse-mu:line-loop/disp32
}
# otherwise abort
e9/jump $parse-mu:error1/disp32
} # end line loop
$parse-mu:end:
# . reclaim locals
81 0/subop/add %esp 0x630/imm32
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-mu:error1:
# error("unexpected top-level command: " word-slice "\n")
(write-buffered Stderr "unexpected top-level command: ")
(write-slice-buffered Stderr %edx)
(write-buffered Stderr "\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$parse-mu:error2:
# error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
(print-int32-buffered Stderr *ebx)
(write-buffered Stderr " vars not reclaimed after fn '")
(write-slice-buffered Stderr *eax) # Function-name
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
# scenarios considered:
# ✗ fn foo # no block
# ✓ fn foo {
# ✗ fn foo { {
# ✗ fn foo { }
# ✗ fn foo { } {
# ✗ fn foo x {
# ✗ fn foo x: {
# ✓ fn foo x: int {
# ✓ fn foo x: int {
# ✓ fn foo x: int -> y/eax: int {
populate-mu-function-header: # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
# pseudocode:
# var name: slice
# next-mu-token(first-line, name)
# assert(name not in '{' '}' '->')
# out->name = slice-to-string(name)
# var next-offset: int = 8
# ## inouts
# while true
# ## name
# name = next-mu-token(first-line)
# if (name == '{') goto done
# if (name == '->') break
# assert(name != '}')
# var v: (handle var) = parse-var-with-type(name, first-line)
# assert(v->register == null)
# v->stack-offset = next-offset
# next-offset += size-of(v)
# # v->block-depth is implicitly 0
# out->inouts = append(out->inouts, v)
# push(vars, v)
# ## outputs
# while true
# ## name
# name = next-mu-token(first-line)
# assert(name not in '{' '}' '->')
# var v: (handle var) = parse-var-with-type(name, first-line)
# assert(v->register != null)
# out->outputs = append(out->outputs, v)
# done:
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
57/push-edi
# edi = out
8b/-> *(ebp+0xc) 7/r32/edi
# var word-slice/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# var next-offset/edx = 8
ba/copy-to-edx 8/imm32
# read function name
(next-mu-token *(ebp+8) %ecx)
# error checking
# TODO: error if name starts with 'break' or 'loop'
# if (word-slice == '{') abort
(slice-equal? %ecx "{") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
# if (word-slice == '->') abort
(slice-equal? %ecx "->") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
# if (word-slice == '}') abort
(slice-equal? %ecx "}") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
# save function name
(slice-to-string Heap %ecx) # => eax
89/<- *edi 0/r32/eax # Function-name
# initialize default subx-name as well
89/<- *(edi+4) 0/r32/eax # Function-subx-name
# save function inouts
{
$populate-mu-function-header:check-for-inout:
(next-mu-token *(ebp+8) %ecx)
# if (word-slice == '{') goto done
(slice-equal? %ecx "{") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:done/disp32
# if (word-slice == '->') break
(slice-equal? %ecx "->") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if (word-slice == '}') abort
(slice-equal? %ecx "}") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
# var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
(parse-var-with-type %ecx *(ebp+8)) # => eax
89/<- %ebx 0/r32/eax
# assert(v->register == null)
81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register
0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
# v->stack-offset = next-offset
89/<- *(ebx+0xc) 2/r32/edx # Var-offset
# next-offset += size-of(v)
(size-of %ebx) # => eax
01/add %edx 0/r32/eax
# v->block-depth is implicitly 0
#
# out->inouts = append(out->inouts, v)
(append-list Heap %ebx *(edi+8)) # Function-inouts => eax
89/<- *(edi+8) 0/r32/eax # Function-inouts
# push(vars, v)
(push *(ebp+0x10) %ebx)
#
e9/jump loop/disp32
}
# save function outputs
{
$populate-mu-function-header:check-for-out:
(next-mu-token *(ebp+8) %ecx)
# if (word-slice == '{') break
(slice-equal? %ecx "{") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if (word-slice == '->') abort
(slice-equal? %ecx "->") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
# if (word-slice == '}') abort
(slice-equal? %ecx "}") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
#
(parse-var-with-type %ecx *(ebp+8)) # => eax
89/<- %ebx 0/r32/eax
# assert(var->register != null)
81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register
0f 84/jump-if-= $populate-mu-function-header:error3/disp32
(append-list Heap %ebx *(edi+0xc)) # Function-outputs => eax
89/<- *(edi+0xc) 0/r32/eax # Function-outputs
e9/jump loop/disp32
}
$populate-mu-function-header:done:
(check-no-tokens-left *(ebp+8))
$populate-mu-function-header:end:
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5f/pop-to-edi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$populate-mu-function-header:error1:
# error("function header not in form 'fn <name> {'")
(write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
(flush Stderr)
(rewind-stream *(ebp+8))
(write-stream 2 *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$populate-mu-function-header:error2:
# error("function input '" var "' cannot be in a register")
(write-buffered Stderr "function input '")
(write-buffered Stderr *ebx) # Var-name
(write-buffered Stderr "' cannot be in a register")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$populate-mu-function-header:error3:
# error("function input '" var "' must be in a register")
(write-buffered Stderr "function input '")
(write-buffered Stderr *eax) # Var-name
(write-buffered Stderr " must be in a register'")
(flush Stderr)
(rewind-stream *(ebp+8))
(write-stream 2 *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
test-function-header-with-arg:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "foo n: int {\n")
# var result/ecx: function
2b/subtract-> *Function-size 4/r32/esp
89/<- %ecx 4/r32/esp
(zero-out %ecx *Function-size)
# var vars/ebx: (stack (addr var) 16)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ebx 4/r32/esp
# convert
(populate-mu-function-header _test-input-stream %ecx %ebx)
# check result
(check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name") # Function-name
# edx: (handle list var) = result->inouts
8b/-> *(ecx+8) 2/r32/edx # Function-inouts
# ebx: (handle var) = result->inouts->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1") # Tree-right
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-function-header-with-multiple-args:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "foo a: int, b: int c: int {\n")
# result/ecx: (handle function)
2b/subtract-> *Function-size 4/r32/esp
89/<- %ecx 4/r32/esp
(zero-out %ecx *Function-size)
# var vars/ebx: (stack (addr var) 16)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ebx 4/r32/esp
# convert
(populate-mu-function-header _test-input-stream %ecx %ebx)
# check result
(check-strings-equal *ecx "foo") # Function-name
# edx: (handle list var) = result->inouts
8b/-> *(ecx+8) 2/r32/edx # Function-inouts
$test-function-header-with-multiple-args:inout0:
# ebx: (handle var) = result->inouts->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1") # Tree-right
# edx = result->inouts->next
8b/-> *(edx+4) 2/r32/edx # List-next
$test-function-header-with-multiple-args:inout1:
# ebx = result->inouts->next->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1") # Tree-right
# edx = result->inouts->next->next
8b/-> *(edx+4) 2/r32/edx # List-next
$test-function-header-with-multiple-args:inout2:
# ebx = result->inouts->next->next->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1") # Tree-right
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-function-with-multiple-args-and-outputs:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
# result/ecx: (handle function)
2b/subtract-> *Function-size 4/r32/esp
89/<- %ecx 4/r32/esp
(zero-out %ecx *Function-size)
# var vars/ebx: (stack (addr var) 16)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ebx 4/r32/esp
# convert
(populate-mu-function-header _test-input-stream %ecx %ebx)
# check result
(check-strings-equal *ecx "foo") # Function-name
# edx: (handle list var) = result->inouts
8b/-> *(ecx+8) 2/r32/edx # Function-inouts
# ebx: (handle var) = result->inouts->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1") # Tree-right
# edx = result->inouts->next
8b/-> *(edx+4) 2/r32/edx # List-next
# ebx = result->inouts->next->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1") # Tree-right
# edx = result->inouts->next->next
8b/-> *(edx+4) 2/r32/edx # List-next
# ebx = result->inouts->next->next->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2") # Var-name
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1") # Tree-right
# edx: (handle list var) = result->outputs
8b/-> *(ecx+0xc) 2/r32/edx # Function-outputs
# ebx: (handle var) = result->outputs->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0") # Var-name
(check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register") # Var-register
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1") # Tree-right
# edx = result->outputs->next
8b/-> *(edx+4) 2/r32/edx # List-next
# ebx = result->outputs->next->value
8b/-> *edx 3/r32/ebx # List-value
(check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1") # Var-name
(check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register") # Var-register
8b/-> *(ebx+4) 3/r32/ebx # Var-type
(check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1") # Tree-left
(check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1") # Tree-right
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# format for variables with types
# x: int
# x: int,
# x/eax: int
# x/eax: int,
# ignores at most one trailing comma
# WARNING: modifies name
parse-var-with-type: # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
# pseudocode:
# var v: (handle var) = allocate(Heap, Var-size)
# var s: slice
# if (!slice-ends-with(name, ":"))
# abort
# --name->end to skip ':'
# next-token-from-slice(name->start, name->end, '/', s)
# v->name = slice-to-string(s)
# ## register
# next-token-from-slice(s->end, name->end, '/', s)
# if (!slice-empty?(s))
# v->register = slice-to-string(s)
# ## type
# var type: (handle tree type-id) = parse-type(first-line)
# v->type = type
# return v
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# esi = name
8b/-> *(ebp+8) 6/r32/esi
# if (!slice-ends-with?(name, ":")) abort
8b/-> *(esi+4) 1/r32/ecx # Slice-end
49/decrement-ecx
8a/copy-byte *ecx 1/r32/CL
81 4/subop/and %ecx 0xff/imm32
81 7/subop/compare %ecx 0x3a/imm32/colon
0f 85/jump-if-!= $parse-var-with-type:abort/disp32
# --name->end to skip ':'
ff 1/subop/decrement *(esi+4)
# var result/edi: (handle var) = allocate(Heap, Var-size)
(allocate Heap *Var-size) # => eax
(zero-out %eax *Var-size)
89/<- %edi 0/r32/eax
# var s/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
$parse-var-with-type:save-name:
# save v->name
(next-token-from-slice *esi *(esi+4) 0x2f %ecx) # Slice-start, Slice-end, '/'
# . end/edx = s->end
8b/-> *(ecx+4) 2/r32/edx
$parse-var-with-type:write-name:
(slice-to-string Heap %ecx) # => eax
89/<- *edi 0/r32/eax # Var-name
# save v->register
$parse-var-with-type:save-register:
(next-token-from-slice %edx *(esi+4) 0x2f %ecx) # end, name->end, '/'
# if (!slice-empty?(s)) v->register = slice-to-string(s)
{
$parse-var-with-type:write-register:
(slice-empty? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
(slice-to-string Heap %ecx)
89/<- *(edi+0x10) 0/r32/eax # Var-register
}
$parse-var-with-type:save-type:
(parse-type Heap *(ebp+0xc)) # => eax
89/<- *(edi+4) 0/r32/eax # Var-type
$parse-var-with-type:end:
# return result
89/<- %eax 7/r32/edi
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-var-with-type:abort:
# error("var should have form 'name: type' in '" line "'\n")
(write-buffered Stderr "var should have form 'name: type' in '")
(flush Stderr)
(rewind-stream *(ebp+0xc))
(write-stream 2 *(ebp+0xc))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
parse-type: # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
# pseudocode:
# var s: slice = next-mu-token(in)
# assert s != ""
# assert s != "->"
# assert s != "{"
# assert s != "}"
# if s == ")"
# return 0
# result = allocate(Tree)
# zero-out(result, *Tree-size)
# if s != "("
# result->left = pos-or-insert-slice(Type-id, s)
# return
# result->left = parse-type(ad, in)
# result->right = parse-type-tree(ad, in)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
# var s/ecx: slice
68/push 0/imm32
68/push 0/imm32
89/<- %ecx 4/r32/esp
# s = next-mu-token(in)
(next-mu-token *(ebp+0xc) %ecx)
#? (write-buffered Stderr "tok: ")
#? (write-slice-buffered Stderr %ecx)
#? (write-buffered Stderr "$\n")
#? (flush Stderr)
# assert s != ""
(slice-equal? %ecx "")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $parse-type:abort/disp32
# assert s != "{"
(slice-equal? %ecx "{")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $parse-type:abort/disp32
# assert s != "}"
(slice-equal? %ecx "}")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $parse-type:abort/disp32
# assert s != "->"
(slice-equal? %ecx "->")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $parse-type:abort/disp32
# if (s == ")") return 0
(slice-equal? %ecx ")")
3d/compare-eax-and 0/imm32/false
b8/copy-to-eax 0/imm32
0f 85/jump-if-!= $parse-type:end/disp32
# var result/edx: (handle tree type-id)
(allocate *(ebp+8) *Tree-size) # => eax
(zero-out %eax *Tree-size)
89/<- %edx 0/r32/eax
{
# if (s != "(") break
(slice-equal? %ecx "(")
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
# result->left = pos-or-insert-slice(Type-id, s)
(pos-or-insert-slice Type-id %ecx) # => eax
#? (write-buffered Stderr "=> {")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr ", 0}\n")
#? (flush Stderr)
89/<- *edx 0/r32/eax # Tree-left
e9/jump $parse-type:return-edx/disp32
}
# otherwise s == "("
# result->left = parse-type(ad, in)
(parse-type *(ebp+8) *(ebp+0xc))
#? (write-buffered Stderr "=> {")
#? (print-int32-buffered Stderr %eax)
89/<- *edx 0/r32/eax # Tree-left
# result->right = parse-type-tree(ad, in)
(parse-type-tree *(ebp+8) *(ebp+0xc))
#? (write-buffered Stderr Space)
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr "}\n")
#? (flush Stderr)
89/<- *(edx+4) 0/r32/eax # Tree-right
$parse-type:return-edx:
89/<- %eax 2/r32/edx
$parse-type:end:
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-type:abort:
# error("unexpected token when parsing type: '" s "'\n")
(write-buffered Stderr "unexpected token when parsing type: '")
(write-slice-buffered Stderr %ecx)
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
parse-type-tree: # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
# pseudocode:
# var tmp: (handle tree type-id) = parse-type(ad, in)
# if tmp == 0
# return 0
# result = allocate(Tree)
# zero-out(result, *Tree-size)
# result->left = tmp
# result->right = parse-type-tree(ad, in)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
# var tmp/eax: (handle tree type-id) = parse-type(ad, in)
(parse-type *(ebp+8) *(ebp+0xc))
# if (tmp == 0) return tmp
3d/compare-eax-and 0/imm32
74/jump-if-= $parse-type-tree:end/disp8
# var tmp2/ecx = tmp
89/<- %ecx 0/r32/eax
# var result/edx: (handle tree type-id)
(allocate *(ebp+8) *Tree-size) # => eax
(zero-out %eax *Tree-size)
89/<- %edx 0/r32/eax
# result->left = tmp2
89/<- *edx 1/r32/ecx # Tree-left
# result->right = parse-type-tree(ad, in)
(parse-type-tree *(ebp+8) *(ebp+0xc))
89/<- *(edx+4) 0/r32/eax # Tree-right
$parse-type-tree:return-edx:
89/<- %eax 2/r32/edx
$parse-type-tree:end:
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
next-mu-token: # in: (addr stream byte), out: (addr slice)
# pseudocode:
# start:
# skip-chars-matching-whitespace(in)
# if in->read >= in->write # end of in
# out = {0, 0}
# return
# out->start = &in->data[in->read]
# var curr-byte/eax: byte = in->data[in->read]
# if curr->byte == ',' # comment token
# ++in->read
# goto start
# if curr-byte == '#' # comment
# goto done # treat as eof
# if curr-byte == '"' # string literal
# skip-string(in)
# goto done # no metadata
# if curr-byte == '('
# ++in->read
# goto done
# if curr-byte == ')'
# ++in->read
# goto done
# # read a word
# while true
# if in->read >= in->write
# break
# curr-byte = in->data[in->read]
# if curr-byte == ' '
# break
# if curr-byte == '\r'
# break
# if curr-byte == '\n'
# break
# if curr-byte == '('
# break
# if curr-byte == ')'
# break
# if curr-byte == ','
# break
# ++in->read
# done:
# out->end = &in->data[in->read]
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
57/push-edi
# esi = in
8b/-> *(ebp+8) 6/r32/esi
# edi = out
8b/-> *(ebp+0xc) 7/r32/edi
$next-mu-token:start:
(skip-chars-matching-whitespace %esi)
$next-mu-token:check0:
# if (in->read >= in->write) return out = {0, 0}
# . ecx = in->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= in->write) return out = {0, 0}
3b/compare 1/r32/ecx *esi
c7 0/subop/copy *edi 0/imm32
c7 0/subop/copy *(edi+4) 0/imm32
0f 8d/jump-if->= $next-mu-token:end/disp32
# out->start = &in->data[in->read]
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *edi 0/r32/eax
# var curr-byte/eax: byte = in->data[in->read]
31/xor %eax 0/r32/eax
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
{
$next-mu-token:check-for-comma:
# if (curr-byte != ',') break
3d/compare-eax-and 0x2c/imm32/comma
75/jump-if-!= break/disp8
# ++in->read
ff 0/subop/increment *(esi+4)
# restart
e9/jump $next-mu-token:start/disp32
}
{
$next-mu-token:check-for-comment:
# if (curr-byte != '#') break
3d/compare-eax-and 0x23/imm32/pound
75/jump-if-!= break/disp8
# return eof
e9/jump $next-mu-token:done/disp32
}
{
$next-mu-token:check-for-string-literal:
# if (curr-byte != '"') break
3d/compare-eax-and 0x22/imm32/dquote
75/jump-if-!= break/disp8
(skip-string %esi)
# return
e9/jump $next-mu-token:done/disp32
}
{
$next-mu-token:check-for-open-paren:
# if (curr-byte != '(') break
3d/compare-eax-and 0x28/imm32/open-paren
75/jump-if-!= break/disp8
# ++in->read
ff 0/subop/increment *(esi+4)
# return
e9/jump $next-mu-token:done/disp32
}
{
$next-mu-token:check-for-close-paren:
# if (curr-byte != ')') break
3d/compare-eax-and 0x29/imm32/close-paren
75/jump-if-!= break/disp8
# ++in->read
ff 0/subop/increment *(esi+4)
# return
e9/jump $next-mu-token:done/disp32
}
{
$next-mu-token:regular-word-without-metadata:
# if (in->read >= in->write) break
# . ecx = in->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= in->write) break
3b/compare *esi 1/r32/ecx
7d/jump-if->= break/disp8
# var c/eax: byte = in->data[in->read]
31/xor %eax 0/r32/eax
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# if (c == ' ') break
3d/compare-eax-and 0x20/imm32/space
74/jump-if-= break/disp8
# if (c == '\r') break
3d/compare-eax-and 0xd/imm32/carriage-return
74/jump-if-= break/disp8
# if (c == '\n') break
3d/compare-eax-and 0xa/imm32/newline
74/jump-if-= break/disp8
# if (c == '(') break
3d/compare-eax-and 0x28/imm32/open-paren
0f 84/jump-if-= break/disp32
# if (c == ')') break
3d/compare-eax-and 0x29/imm32/close-paren
0f 84/jump-if-= break/disp32
# if (c == ',') break
3d/compare-eax-and 0x2c/imm32/comma
0f 84/jump-if-= break/disp32
# ++in->read
ff 0/subop/increment *(esi+4)
#
e9/jump loop/disp32
}
$next-mu-token:done:
# out->end = &in->data[in->read]
8b/-> *(esi+4) 1/r32/ecx
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *(edi+4) 0/r32/eax
$next-mu-token:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
pos-or-insert-slice: # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# if (pos-slice(arr, s) != -1) return it
(pos-slice *(ebp+8) *(ebp+0xc)) # => eax
3d/compare-eax-and -1/imm32
75/jump-if-not-equal $pos-or-insert-slice:end/disp8
#
(slice-to-string Heap *(ebp+0xc)) # => eax
(write-int *(ebp+8) %eax)
(pos-slice *(ebp+8) *(ebp+0xc)) # => eax
$pos-or-insert-slice:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# return the index in an array of strings matching 's', -1 if not found
# index is denominated in elements, not bytes
pos-slice: # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
#? (write-buffered Stderr "pos-slice: ")
#? (write-slice-buffered Stderr *(ebp+0xc))
#? (write-buffered Stderr "\n")
#? (flush Stderr)
# esi = arr
8b/-> *(ebp+8) 6/r32/esi
# var index/ecx: int = 0
b9/copy-to-ecx 0/imm32
# var curr/edx: (addr (addr array byte)) = arr->data
8d/copy-address *(esi+0xc) 2/r32/edx
# var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
8b/-> *esi 3/r32/ebx
8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
{
#? (write-buffered Stderr " ")
#? (print-int32-buffered Stderr %ecx)
#? (write-buffered Stderr "\n")
#? (flush Stderr)
# if (curr >= max) return -1
39/compare %edx 3/r32/ebx
b8/copy-to-eax -1/imm32
73/jump-if-addr>= $pos-slice:end/disp8
# if (slice-equal?(s, *curr)) break
(slice-equal? *(ebp+0xc) *edx) # => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
# ++index
41/increment-ecx
# curr += 4
81 0/subop/add %edx 4/imm32
#
eb/jump loop/disp8
}
# return index
89/<- %eax 1/r32/ecx
$pos-slice:end:
#? (write-buffered Stderr "=> ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr "\n")
# . restore registers
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-var-with-type:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "x:"
b8/copy-to-eax "x:"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
# _test-input-stream contains "int"
(clear-stream _test-input-stream)
(write _test-input-stream "int")
#
(parse-var-with-type %ecx _test-input-stream)
8b/-> *eax 2/r32/edx # Var-name
(check-strings-equal %edx "x" "F - test-var-with-type/name")
8b/-> *(eax+4) 2/r32/edx # Var-type
(check-ints-equal *edx 1 "F - test-var-with-type/type")
(check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-var-with-type-and-register:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "x/eax:"
b8/copy-to-eax "x/eax:"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
# _test-input-stream contains "int"
(clear-stream _test-input-stream)
(write _test-input-stream "int")
#
(parse-var-with-type %ecx _test-input-stream)
8b/-> *eax 2/r32/edx # Var-name
(check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
8b/-> *(eax+0x10) 2/r32/edx # Var-register
(check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
8b/-> *(eax+4) 2/r32/edx # Var-type
(check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
(check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-var-with-trailing-characters:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "x:"
b8/copy-to-eax "x:"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
# _test-input-stream contains "int,"
(clear-stream _test-input-stream)
(write _test-input-stream "int,")
#
(parse-var-with-type %ecx _test-input-stream)
8b/-> *eax 2/r32/edx # Var-name
(check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
8b/-> *(eax+0x10) 2/r32/edx # Var-register
(check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
8b/-> *(eax+4) 2/r32/edx # Var-type
(check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
(check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-var-with-register-and-trailing-characters:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "x/eax:"
b8/copy-to-eax "x/eax:"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
# _test-input-stream contains "int,"
(clear-stream _test-input-stream)
(write _test-input-stream "int,")
#
(parse-var-with-type %ecx _test-input-stream)
8b/-> *eax 2/r32/edx # Var-name
(check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
8b/-> *(eax+0x10) 2/r32/edx # Var-register
(check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
8b/-> *(eax+4) 2/r32/edx # Var-type
(check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
(check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-var-with-compound-type:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "x:"
b8/copy-to-eax "x:"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
# _test-input-stream contains "(addr int)"
(clear-stream _test-input-stream)
(write _test-input-stream "(addr int)")
#
(parse-var-with-type %ecx _test-input-stream)
8b/-> *eax 2/r32/edx # Var-name
(check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
8b/-> *(eax+0x10) 2/r32/edx # Var-register
(check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
# var type/edx: (handle tree type-id) = var->type
8b/-> *(eax+4) 2/r32/edx # Var-type
# type->left == atom(addr)
8b/-> *edx 0/r32/eax # Atom-value
(check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0") # Tree-left
# type->right->left == atom(int)
8b/-> *(edx+4) 2/r32/edx # Tree-right
8b/-> *edx 0/r32/eax # Tree-left
(check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1") # Atom-value
# type->right->right == null
(check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2") # Tree-right
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# identifier starts with a letter or '$' or '_'
# no constraints at the moment on later letters
# all we really want to do so far is exclude '{', '}' and '->'
is-identifier?: # in: (addr slice) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# if (slice-empty?(in)) return false
(slice-empty? *(ebp+8)) # => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= $is-identifier?:false/disp8
# var c/eax: byte = *in->start
8b/-> *(ebp+8) 0/r32/eax
8b/-> *eax 0/r32/eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# if (c == '$') return true
3d/compare-eax-and 0x24/imm32/$
74/jump-if-= $is-identifier?:true/disp8
# if (c == '_') return true
3d/compare-eax-and 0x5f/imm32/_
74/jump-if-= $is-identifier?:true/disp8
# drop case
25/and-eax-with 0x5f/imm32
# if (c < 'A') return false
3d/compare-eax-and 0x41/imm32/A
7c/jump-if-< $is-identifier?:false/disp8
# if (c > 'Z') return false
3d/compare-eax-and 0x5a/imm32/Z
7f/jump-if-> $is-identifier?:false/disp8
# otherwise return true
$is-identifier?:true:
b8/copy-to-eax 1/imm32/true
eb/jump $is-identifier?:end/disp8
$is-identifier?:false:
b8/copy-to-eax 0/imm32/false
$is-identifier?:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-dollar:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "$a"
b8/copy-to-eax "$a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-dollar")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-underscore:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "_a"
b8/copy-to-eax "_a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-underscore")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-a:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "a$"
b8/copy-to-eax "a$"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-a")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-z:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "z$"
b8/copy-to-eax "z$"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-z")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-A:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "A$"
b8/copy-to-eax "A$"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-A")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-Z:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "Z$"
b8/copy-to-eax "Z$"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 1 "F - test-is-identifier-Z")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-@:
# character before 'A' is invalid
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "@a"
b8/copy-to-eax "@a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-@")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-square-bracket:
# character after 'Z' is invalid
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "[a"
b8/copy-to-eax "[a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-@")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-backtick:
# character before 'a' is invalid
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "`a"
b8/copy-to-eax "`a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-backtick")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-curly-brace-open:
# character after 'z' is invalid; also used for blocks
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "{a"
b8/copy-to-eax "{a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-curly-brace-close:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "}a"
b8/copy-to-eax "}a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-is-identifier-hyphen:
# disallow leading '-' since '->' has special meaning
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# (eax..ecx) = "-a"
b8/copy-to-eax "-a"/imm32
8b/-> *eax 1/r32/ecx
8d/copy-address *(eax+ecx+4) 1/r32/ecx
05/add-to-eax 4/imm32
# var slice/ecx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/<- %ecx 4/r32/esp
#
(is-identifier? %ecx)
(check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
populate-mu-function-body: # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
56/push-esi
57/push-edi
# esi = in
8b/-> *(ebp+8) 6/r32/esi
# edi = out
8b/-> *(ebp+0xc) 7/r32/edi
# initialize some global state
c7 0/subop/copy *Curr-block-depth 1/imm32
c7 0/subop/copy *Next-local-stack-offset -4/imm32
# var eax: (handle block) = parse-mu-block(in, vars, fn)
(parse-mu-block %esi *(ebp+0x10) %edi) # => eax
# out->body = eax
89/<- *(edi+0x10) 0/r32/eax # Function-body
$populate-mu-function-body:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
== data
# Global state added to each var record when parsing a function
Curr-block-depth: # (addr int)
0/imm32
Next-local-stack-offset: # (addr int)
-4/imm32
Next-block-index: # (addr int)
1/imm32
== code
# parses a block, assuming that the leading '{' has already been read by the caller
parse-mu-block: # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
# pseudocode:
# var line: (stream byte 512)
# var word-slice: slice
# increment *Curr-block-depth
# result/eax = allocate(Heap, Stmt-size)
# result->tag = 0/block
# result->name = some unique name
# while true # line loop
# clear-stream(line)
# read-line-buffered(in, line)
# if (line->write == 0) break # end of file
# word-slice = next-mu-token(line)
# if slice-empty?(word-slice) # end of line
# continue
# else if slice-starts-with?(word-slice, "#")
# continue
# else if slice-equal?(word-slice, "{")
# assert(no-tokens-in(line))
# block = parse-mu-block(in, vars, fn)
# append-to-block(result, block)
# else if slice-equal?(word-slice, "}")
# break
# else if slice-ends-with?(word-slice, ":")
# # TODO: error-check the rest of 'line'
# --word-slice->end to skip ':'
# named-block = parse-mu-named-block(word-slice, in, vars, fn)
# append-to-block(result, named-block)
# else if slice-equal?(word-slice, "var")
# var-def = parse-mu-var-def(line, vars)
# append-to-block(result, var-def)
# else
# stmt = parse-mu-stmt(line, vars, fn)
# append-to-block(result, stmt)
# decrement *Curr-block-depth
# return result
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
53/push-ebx
57/push-edi
# var line/ecx: (stream byte 512)
81 5/subop/subtract %esp 0x200/imm32
68/push 0x200/imm32/length
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %ecx 4/r32/esp
# var word-slice/edx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %edx 4/r32/esp
# edi = result
(allocate Heap *Stmt-size) # => eax
(zero-out %eax *Stmt-size)
89/<- %edi 0/r32/eax
# set result->tag
c7 0/subop/copy *edi 0/imm32/block # Stmt-tag
# set result->var
(new-block-name *(ebp+0x10)) # => eax
89/<- *(edi+8) 0/r32/eax # Block-var
# push result->var to vars
(push *(ebp+0xc) %eax)
# increment *Curr-block-depth
ff 0/subop/increment *Curr-block-depth
{
$parse-mu-block:line-loop:
# line = read-line-buffered(in)
(clear-stream %ecx)
(read-line-buffered *(ebp+8) %ecx)
#? (write-buffered Stderr "line: ")
#? (write-stream-data Stderr %ecx)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
# if (line->write == 0) break
81 7/subop/compare *ecx 0/imm32
0f 84/jump-if-= break/disp32
# word-slice = next-mu-token(line)
(next-mu-token %ecx %edx)
#? (write-buffered Stderr "word: ")
#? (write-slice-buffered Stderr %edx)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
# if slice-empty?(word-slice) continue
(slice-empty? %edx)
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= loop/disp32
# if (slice-starts-with?(word-slice, '#') continue
# . eax = *word-slice->start
8b/-> *edx 0/r32/eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# . if (eax == '#') continue
3d/compare-eax-and 0x23/imm32/hash
0f 84/jump-if-= loop/disp32
# if slice-equal?(word-slice, "{")
{
$parse-mu-block:check-for-block:
(slice-equal? %edx "{")
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(check-no-tokens-left %ecx)
# parse new block and append
(parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10)) # => eax
(append-to-block Heap %edi %eax)
e9/jump $parse-mu-block:line-loop/disp32
}
# if slice-equal?(word-slice, "}") break
$parse-mu-block:check-for-end:
(slice-equal? %edx "}")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if slice-ends-with?(word-slice, ":") parse named block and append
{
$parse-mu-block:check-for-named-block:
# . eax = *(word-slice->end-1)
8b/-> *(edx+4) 0/r32/eax
48/decrement-eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# . if (eax != ':') break
3d/compare-eax-and 0x3a/imm32/colon
0f 85/jump-if-!= break/disp32
# TODO: error-check the rest of 'line'
#
# skip ':'
ff 1/subop/decrement *(edx+4) # Slice-end
#
(parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10)) # => eax
(append-to-block Heap %edi %eax)
e9/jump $parse-mu-block:line-loop/disp32
}
# if slice-equal?(word-slice, "var")
{
$parse-mu-block:check-for-var:
(slice-equal? %edx "var")
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
#
(parse-mu-var-def %ecx *(ebp+0xc)) # => eax
(append-to-block Heap %edi %eax)
e9/jump $parse-mu-block:line-loop/disp32
}
$parse-mu-block:regular-stmt:
# otherwise
(parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10)) # => eax
(append-to-block Heap %edi %eax)
e9/jump loop/disp32
} # end line loop
# decrement *Curr-block-depth
ff 1/subop/decrement *Curr-block-depth
#
(pop *(ebp+0xc)) # => eax
# return result
89/<- %eax 7/r32/edi
$parse-mu-block:end:
# . reclaim locals
81 0/subop/add %esp 0x214/imm32
# . restore registers
5f/pop-to-edi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-mu-block:abort:
# error("'{' or '}' should be on its own line, but got '")
(write-buffered Stderr "'{' or '}' should be on its own line, but got '")
(rewind-stream %ecx)
(write-stream 2 %ecx)
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
new-block-name: # fn: (handle function) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
# var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
8b/-> *(ebp+8) 0/r32/eax
8b/-> *eax 0/r32/eax # Function-name
8b/-> *eax 0/r32/eax # String-length
05/add-to-eax 0xd/imm32 # 10 + 2 for '$:'
89/<- %ecx 0/r32/eax
# var name/edx: (stream byte n)
29/subtract %esp 1/r32/ecx
ff 6/subop/push %ecx
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %edx 4/r32/esp
(clear-stream %edx)
# eax = fn->name
8b/-> *(ebp+8) 0/r32/eax
8b/-> *eax 0/r32/eax # Function-name
# construct result using Next-block-index (and increment it)
(write %edx "$")
(write %edx %eax)
(write %edx ":")
(print-int32 %edx *Next-block-index)
ff 0/subop/increment *Next-block-index
# var s/eax: slice = {name->data, name->data + name->write} (clobbering edx)
# . eax = name->write
8b/-> *edx 0/r32/eax
# . edx = name->data
8d/copy-address *(edx+0xc) 2/r32/edx
# . eax = name->write + name->data
01/add %eax 2/r32/edx
# . push {edx, eax}
ff 6/subop/push %eax
ff 6/subop/push %edx
89/<- %eax 4/r32/esp
# result->var = new literal(s)
(new-literal Heap %eax) # => eax
$new-block-name:end:
# . reclaim locals
81 0/subop/add %ecx 0xc/imm32 # name.{read/write/len}
81 0/subop/add %ecx 8/imm32 # slice
01/add %esp 1/r32/ecx
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
check-no-tokens-left: # line: (addr stream byte)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# var s/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
#
(next-mu-token *(ebp+8) %ecx)
# if slice-empty?(s) return
(slice-empty? %ecx)
3d/compare-eax-and 0/imm32/false
75/jump-if-!= $check-no-tokens-left:end/disp8
# if (slice-starts-with?(s, '#') return
# . eax = *s->start
8b/-> *edx 0/r32/eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# . if (eax == '#') continue
3d/compare-eax-and 0x23/imm32/hash
74/jump-if-= $check-no-tokens-left:end/disp8
# abort
(write-buffered Stderr "'{' or '}' should be on its own line, but got '")
(rewind-stream %ecx)
(write-stream 2 %ecx)
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$check-no-tokens-left:end:
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
parse-mu-named-block: # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
# pseudocode:
# var s: (addr array byte) = slice-to-string(name)
# var v: (handle var) = new-var(s, 0)
# v->block-depth = *Curr-block-depth # containing block depth
# push(vars, v)
# result = parse-mu-block(in, vars, fn)
# pop(vars)
# result->name = s
# return result
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var v/ecx: (handle var)
(new-literal Heap *(ebp+8)) # => eax
89/<- %ecx 0/r32/eax
# push(vars, v)
(push *(ebp+0x10) %ecx)
# eax = result
(parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14)) # => eax
# pop the var
50/push-eax
(pop *(ebp+0x10)) # => eax
58/pop-to-eax
# result->tag = named-block
c7 0/subop/copy *eax 0/imm32/block # Stmt-tag
# result->var = v
89/<- *(eax+8) 1/r32/ecx # Block-var
$parse-mu-named-block:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
parse-mu-var-def: # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
# var word-slice/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# var v/edx: (handle var) = parse-var-with-type(line)
(next-mu-token *(ebp+8) %ecx)
(parse-var-with-type %ecx *(ebp+8)) # => eax
89/<- %edx 0/r32/eax
# v->block-depth = *Curr-block-depth
8b/-> *Curr-block-depth 0/r32/eax
89/<- *(edx+8) 0/r32/eax
#
(push *(ebp+0xc) %edx)
# either v has no register and there's no more to this line
8b/-> *(edx+0x10) 0/r32/eax # Var-register
3d/compare-eax-and 0/imm32
{
75/jump-if-!= break/disp8
# v->stack-offset = *Next-local-stack-offset
8b/-> *Next-local-stack-offset 0/r32/eax
89/<- *(edx+0xc) 0/r32/eax # Var-offset
# TODO: ensure that there's nothing else on this line
(new-var-def Heap %edx) # => eax
eb/jump $parse-mu-var-def:end/disp8
}
# or v has a register and there's more to this line
{
74/jump-if-= break/disp8
# ensure that the next word is '<-'
(next-mu-token *(ebp+8) %ecx)
(slice-equal? %ecx "<-") # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= $parse-mu-var-def:abort/disp8
#
(new-reg-var-def Heap %edx) # => eax
(add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
}
$parse-mu-var-def:end:
# *Next-local-stack-offset -= size-of(v)
50/push-eax
(size-of %edx) # => eax
29/subtract-from *Next-local-stack-offset 0/r32/eax
58/pop-to-eax
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-mu-var-def:abort:
(rewind-stream *(ebp+8))
# error("register variable requires a valid instruction to initialize but got '" line "'\n")
(write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
(flush Stderr)
(write-stream 2 *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
test-parse-mu-var-def:
# 'var n: int'
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "n: int\n") # caller has consumed the 'var'
c7 0/subop/copy *Curr-block-depth 1/imm32
c7 0/subop/copy *Next-local-stack-offset -4/imm32
# var vars/ecx: (stack (addr var) 4)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ecx 4/r32/esp
(clear-stack %ecx)
# convert
(parse-mu-var-def _test-input-stream %ecx) # => eax
# check result
(check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag") # Stmt-tag is var-def
8b/-> *(eax+4) 0/r32/eax # Vardef-var
(check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name") # Var-name
(check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register") # Var-register
(check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth") # Var-block-depth
(check-ints-equal *(eax+0xc) -4 "F - test-parse-mu-reg-var-def/output-stack-offset") # Var-offset
# ensure type is int
8b/-> *(eax+4) 0/r32/eax # Var-type
(check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0") # Tree-left
(check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0") # Tree-right
# globals
(check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-mu-reg-var-def:
# 'var n/eax: int <- copy 0'
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "n/eax: int <- copy 0\n") # caller has consumed the 'var'
c7 0/subop/copy *Curr-block-depth 1/imm32
c7 0/subop/copy *Next-local-stack-offset -4/imm32
# var vars/ecx: (stack (addr var) 4)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ecx 4/r32/esp
(clear-stack %ecx)
# convert
(parse-mu-var-def _test-input-stream %ecx) # => eax
# check result
(check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag") # Stmt-tag is reg-var-def
8b/-> *(eax+0xc) 0/r32/eax # Regvardef-outputs
(check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output") # List-next
8b/-> *eax 0/r32/eax # Stmt-var-value
(check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name") # Var-name
(check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register") # Var-register
(check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth") # Var-block-depth
(check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-stack-offset") # Var-offset
# ensure type is int
8b/-> *(eax+4) 0/r32/eax # Var-type
(check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0") # Tree-left
(check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0") # Tree-right
# globals
(check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
parse-mu-stmt: # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
# pseudocode:
# var name: slice
# result = allocate(Heap, Stmt-size)
# if stmt-has-outputs?(line)
# while true
# name = next-mu-token(line)
# if (name == '<-') break
# assert(is-identifier?(name))
# var v: (handle var) = lookup-or-define-var(name, vars, fn) # regular stmts may define vars in fn outputs
# result->outputs = append(result->outputs, v)
# add-operation-and-inputs-to-stmt(result, line, vars)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
57/push-edi
# var name/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# var is-deref?/edx: boolean = false
ba/copy-to-edx 0/imm32/false
# result/edi: (handle stmt)
(allocate Heap *Stmt-size) # => eax
(zero-out %eax *Stmt-size)
89/<- %edi 0/r32/eax
# result->tag = 1/stmt
c7 0/subop/copy *edi 1/imm32/stmt1 # Stmt-tag
{
(stmt-has-outputs? *(ebp+8))
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
{
$parse-mu-stmt:read-outputs:
# name = next-mu-token(line)
(next-mu-token *(ebp+8) %ecx)
# if slice-empty?(word-slice) break
(slice-empty? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if (name == "<-") break
(slice-equal? %ecx "<-") # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# is-deref? = false
ba/copy-to-edx 0/imm32/false
# if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
8b/-> *ecx 0/r32/eax # Slice-start
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
3d/compare-eax-and 0x2a/imm32/asterisk
{
75/jump-if-!= break/disp8
ff 0/subop/increment *ecx
ba/copy-to-edx 1/imm32/true
}
# assert(is-identifier?(name))
(is-identifier? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= $parse-mu-stmt:abort/disp32
# result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
(lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10)) # => eax
(append-stmt-var Heap %eax *(edi+0xc) %edx) # Stmt1-outputs => eax
89/<- *(edi+0xc) 0/r32/eax # Stmt1-outputs
e9/jump loop/disp32
}
}
(add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
$parse-mu-stmt:end:
# return result
89/<- %eax 7/r32/edi
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5f/pop-to-edi
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$parse-mu-stmt:abort:
# error("invalid identifier '" name "'\n")
(write-buffered Stderr "invalid identifier '")
(write-slice-buffered Stderr %ecx)
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
add-operation-and-inputs-to-stmt: # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
# pseudocode:
# stmt->name = slice-to-string(next-mu-token(line))
# while true
# name = next-mu-token(line)
# v = lookup-var-or-literal(name)
# stmt->inouts = append(stmt->inouts, v)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
57/push-edi
# edi = stmt
8b/-> *(ebp+8) 7/r32/edi
# var name/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# var is-deref?/edx: boolean = false
ba/copy-to-edx 0/imm32/false
$add-operation-and-inputs-to-stmt:read-operation:
(next-mu-token *(ebp+0xc) %ecx)
(slice-to-string Heap %ecx) # => eax
89/<- *(edi+4) 0/r32/eax # Stmt1-operation or Regvardef-operation
# var is-get?/ebx: boolean = (name == "get")
(slice-equal? %ecx "get") # => eax
89/<- %ebx 0/r32/eax
{
$add-operation-and-inputs-to-stmt:read-inouts:
# name = next-mu-token(line)
(next-mu-token *(ebp+0xc) %ecx)
# if slice-empty?(word-slice) break
(slice-empty? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if (name == "<-") abort
(slice-equal? %ecx "<-")
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
# if (is-get? && second operand) lookup or create offset
{
81 7/subop/compare %ebx 0/imm32/false
74/jump-if-= break/disp8
81 7/subop/compare *(edi+8) 0/imm32 # Stmt1-inouts or Regvardef-inouts
74/jump-if-= break/disp8
(lookup-or-create-constant *(edi+8) %ecx) # Stmt1-inouts => eax
#? (write-buffered Stderr "creating new output var ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr " for field called ")
#? (write-slice-buffered Stderr %ecx)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
}
# is-deref? = false
ba/copy-to-edx 0/imm32/false
# if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
8b/-> *ecx 0/r32/eax # Slice-start
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
3d/compare-eax-and 0x2a/imm32/asterisk
{
75/jump-if-!= break/disp8
$add-operation-and-inputs-to-stmt:inout-is-deref:
ff 0/subop/increment *ecx
ba/copy-to-edx 1/imm32/true
}
(lookup-var-or-literal %ecx *(ebp+0x10)) # => eax
$add-operation-and-inputs-to-stmt:save-var:
(append-stmt-var Heap %eax *(edi+8) %edx) # Stmt1-inouts or Regvardef-inouts => eax
89/<- *(edi+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
e9/jump loop/disp32
}
$add-operation-and-inputs-to-stmt:end:
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
5f/pop-to-edi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$add-operation-and-inputs-to-stmt:abort:
# error("invalid statement '" line "'\n")
(rewind-stream *(ebp+8))
(write-buffered Stderr "invalid identifier '")
(flush Stderr)
(write-stream 2 *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
stmt-has-outputs?: # line: (addr stream byte) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var word-slice/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# result = false
b8/copy-to-eax 0/imm32/false
(rewind-stream *(ebp+8))
{
(next-mu-token *(ebp+8) %ecx)
# if slice-empty?(word-slice) break
(slice-empty? %ecx)
3d/compare-eax-and 0/imm32/false
b8/copy-to-eax 0/imm32/false/result # restore result (if we're here it's still false)
0f 85/jump-if-!= break/disp32
# if slice-starts-with?(word-slice, '#') break
# . eax = *word-slice->start
8b/-> *ecx 0/r32/eax
8a/copy-byte *eax 0/r32/AL
81 4/subop/and %eax 0xff/imm32
# . if (eax == '#') break
3d/compare-eax-and 0x23/imm32/hash
b8/copy-to-eax 0/imm32/false/result # restore result (if we're here it's still false)
0f 84/jump-if-= break/disp32
# if slice-equal?(word-slice, '<-') return true
(slice-equal? %ecx "<-")
3d/compare-eax-and 0/imm32/false
74/jump-if-= loop/disp8
b8/copy-to-eax 1/imm32/true
}
$stmt-has-outputs:end:
(rewind-stream *(ebp+8))
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# if 'name' starts with a digit, create a new literal var for it
# otherwise return first 'name' from the top (back) of 'vars' and abort if not found
lookup-var-or-literal: # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
56/push-esi
# esi = name
8b/-> *(ebp+8) 6/r32/esi
# if slice-empty?(name) abort
(slice-empty? %esi) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
# var c/ecx: byte = *name->start
8b/-> *esi 1/r32/ecx
8a/copy-byte *ecx 1/r32/CL
81 4/subop/and %ecx 0xff/imm32
# if is-decimal-digit?(c) return new var(name)
{
(is-decimal-digit? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(new-literal-integer Heap %esi) # => eax
eb/jump $lookup-var-or-literal:end/disp8
}
# else if (c == '"') return new var(name)
{
81 7/subop/compare %ecx 0x22/imm32/dquote
75/jump-if-!= break/disp8
(new-literal Heap %esi) # => eax
eb/jump $lookup-var-or-literal:end/disp8
}
# otherwise return lookup-var(name, vars)
{
(lookup-var %esi *(ebp+0xc)) # => eax
}
$lookup-var-or-literal:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$lookup-var-or-literal:abort:
(write-buffered Stderr "empty variable!")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
# return first 'name' from the top (back) of 'vars' and abort if not found
lookup-var: # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# var target/eax: (handle array byte) = slice-to-string(name)
(slice-to-string Heap *(ebp+8)) # => eax
#
(lookup-var-helper %eax *(ebp+0xc)) # => eax
# if (result == 0) abort
3d/compare-eax-and 0/imm32
74/jump-if-= $lookup-var:abort/disp8
$lookup-var:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$lookup-var:abort:
(write-buffered Stderr "unknown variable '")
(write-slice-buffered Stderr *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
# return first 'name' from the top (back) of 'vars', and 0/null if not found
lookup-var-helper: # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
# pseudocode:
# var curr: (addr handle var) = &vars->data[vars->top - 4]
# var min = vars->data
# while curr >= min
# var v: (handle var) = *curr
# if v->name == name
# return v
# return 0
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
52/push-edx
53/push-ebx
56/push-esi
# esi = vars
8b/-> *(ebp+0xc) 6/r32/esi
# ebx = vars->top
8b/-> *esi 3/r32/ebx
# if (vars->top > vars->length) abort
3b/compare 0/r32/eax *(esi+4)
0f 8f/jump-if-> $lookup-var-helper:error1/disp32
# var min/edx: (addr handle var) = vars->data
8d/copy-address *(esi+8) 2/r32/edx
# var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
81 5/subop/subtract %ebx 4/imm32
8d/copy-address *(esi+ebx+8) 3/r32/ebx
{
# if (curr < min) return 0
39/compare %ebx 2/r32/edx
b8/copy-to-eax 0/imm32
0f 82/jump-if-addr< break/disp32
# var v/eax: (handle var) = *curr
8b/-> *ebx 0/r32/eax
# if (v->name == name) return v
(string-equal? *eax *(ebp+8)) # Var-name
3d/compare-eax-and 0/imm32/false
8b/-> *ebx 0/r32/eax
75/jump-if-!= break/disp8
# curr -= 4
81 5/subop/subtract %ebx 4/imm32
e9/jump loop/disp32
}
$lookup-var-helper:end:
# . restore registers
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$lookup-var-helper:error1:
(write-buffered Stderr "malformed stack when looking up '")
(write-slice-buffered Stderr *(ebp+8))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
# return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
lookup-or-define-var: # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var target/ecx: (handle array byte) = slice-to-string(name)
(slice-to-string Heap *(ebp+8)) # => eax
89/<- %ecx 0/r32/eax
#
(lookup-var-helper %ecx *(ebp+0xc)) # => eax
{
# if (result != 0) return
3d/compare-eax-and 0/imm32
75/jump-if-!= break/disp8
# if name is one of fn's outputs, return it
{
(find-in-function-outputs *(ebp+0x10) %ecx) # => eax
3d/compare-eax-and 0/imm32
# otherwise abort
0f 84/jump-if-!= $lookup-var:abort/disp32
}
}
$lookup-or-define-var:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-in-function-outputs: # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var curr/ecx: (handle list var) = fn->outputs
8b/-> *(ebp+8) 1/r32/ecx
8b/-> *(ecx+0xc) 1/r32/ecx
# while curr != null
{
81 7/subop/compare %ecx 0/imm32
74/jump-if-= break/disp8
# var v: (handle var) = *curr
8b/-> *ecx 0/r32/eax # List-value
# if (curr->name == name) return curr
50/push-eax
(string-equal? *eax *(ebp+0xc))
3d/compare-eax-and 0/imm32/false
58/pop-to-eax
75/jump-if-!= $find-in-function-outputs:end/disp8
# curr = curr->next
8b/-> *(ecx+4) 1/r32/ecx # List-next
eb/jump loop/disp8
}
b8/copy-to-eax 0/imm32
$find-in-function-outputs:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-mu-stmt:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "increment n\n")
# var vars/ecx: (stack (addr var) 4)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ecx 4/r32/esp
(clear-stack %ecx)
# var v/edx: var
81 5/subop/subtract %esp 0x14/imm32 # Var-size
89/<- %edx 4/r32/esp
(zero-out %edx 0x14) # Var-size
# v->name = "n"
c7 0/subop/copy *edx "n"/imm32 # Var-name
#
(push %ecx %edx)
# convert
(parse-mu-stmt _test-input-stream %ecx) # => eax
# check result
(check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag") # Stmt-tag is Stmt1
(check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name") # Stmt1-operation
# edx: (handle list var) = result->inouts
8b/-> *(eax+8) 2/r32/edx # Stmt1-inouts
# ebx: (handle var) = result->inouts->value
8b/-> *edx 3/r32/ebx # Stmt-var-value
(check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0") # Var-name
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-parse-mu-stmt-with-comma:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-input-stream)
(write _test-input-stream "copy-to n, 3\n")
# var vars/ecx: (stack (addr var) 4)
81 5/subop/subtract %esp 0x10/imm32
68/push 0x10/imm32/length
68/push 0/imm32/top
89/<- %ecx 4/r32/esp
(clear-stack %ecx)
# var v/edx: var
81 5/subop/subtract %esp 0x14/imm32 # Var-size
89/<- %edx 4/r32/esp
(zero-out %edx 0x14) # Var-size
# v->name = "n"
c7 0/subop/copy *edx "n"/imm32 # Var-name
#
(push %ecx %edx)
# convert
(parse-mu-stmt _test-input-stream %ecx) # => eax
# check result
(check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag") # Stmt-tag is Stmt1
(check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name") # Stmt1-operation
# edx: (handle list var) = result->inouts
8b/-> *(eax+8) 2/r32/edx # Stmt1-inouts
# ebx: (handle var) = result->inouts->value
8b/-> *edx 3/r32/ebx # Stmt-var-value
(check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0") # Var-name
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-function: # ad: (addr allocation-descriptor), name: (addr array byte), subx-name: (addr array byte), inouts: (handle list var), outputs: (handle list var), body: (handle block), next: (handle function) -> result/eax: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *Function-size) # => eax
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *eax 1/r32/ecx # Function-name
8b/-> *(ebp+0x10) 1/r32/ecx
89/<- *(eax+4) 1/r32/ecx # Function-subx-name
8b/-> *(ebp+0x14) 1/r32/ecx
89/<- *(eax+8) 1/r32/ecx # Function-inouts
8b/-> *(ebp+0x18) 1/r32/ecx
89/<- *(eax+0xc) 1/r32/ecx # Function-outputs
8b/-> *(ebp+0x1c) 1/r32/ecx
89/<- *(eax+0x10) 1/r32/ecx # Function-body
8b/-> *(ebp+0x20) 1/r32/ecx
89/<- *(eax+0x14) 1/r32/ecx # Function-next
$new-function:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-var: # ad: (addr allocation-descriptor), name: (addr array byte), type: (addr tree type-id), block: int, offset: int, register: (addr array byte) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *Var-size) # => eax
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *eax 1/r32/ecx # Var-name
8b/-> *(ebp+0x10) 1/r32/ecx
89/<- *(eax+4) 1/r32/ecx # Var-type
8b/-> *(ebp+0x14) 1/r32/ecx
89/<- *(eax+8) 1/r32/ecx # Var-block-depth
8b/-> *(ebp+0x18) 1/r32/ecx
89/<- *(eax+0xc) 1/r32/ecx # Var-offset
8b/-> *(ebp+0x1c) 1/r32/ecx
89/<- *(eax+0x10) 1/r32/ecx # Var-register
$new-var:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-literal-integer: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# if (!is-hex-int?(name)) abort
(is-hex-int? *(ebp+0xc)) # => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= $new-literal-integer:abort/disp32
# var s/ecx: (addr array byte)
(slice-to-string Heap *(ebp+0xc)) # => eax
89/<- %ecx 0/r32/eax
# result/ecx = new var(s)
(allocate *(ebp+8) *Var-size) # => eax
(zero-out %eax *Var-size)
89/<- *eax 1/r32/ecx # Var-name
89/<- %ecx 0/r32/eax
# result->type = new type()
(allocate *(ebp+8) *Tree-size) # => eax
(zero-out %eax *Tree-size) # default type is 'literal'
89/<- *(ecx+4) 0/r32/eax # Var-type
# move result to eax
89/<- %eax 1/r32/ecx
$new-literal-integer:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$new-literal-integer:abort:
(write-buffered Stderr "variable cannot begin with a digit '")
(write-slice-buffered Stderr *(ebp+0xc))
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
new-literal: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var s/ecx: (addr array byte)
(slice-to-string Heap *(ebp+0xc)) # => eax
89/<- %ecx 0/r32/eax
# result->type = new type()
(allocate *(ebp+8) *Tree-size) # => eax
(zero-out %eax *Tree-size) # default type is 'literal'
#
(new-var *(ebp+8) %ecx %eax *Curr-block-depth 0 0) # => eax
$new-literal:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-label: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var s/ecx: (addr array byte)
(slice-to-string Heap *(ebp+0xc)) # => eax
89/<- %ecx 0/r32/eax
#
(allocate *(ebp+8) *Var-size) # => eax
89/<- *eax 1/r32/ecx # Var-name
89/<- %ecx 0/r32/eax
(allocate *(ebp+8) *Tree-size) # => eax
(zero-out %eax *Tree-size) # labels are literals
89/<- *(ecx+4) 0/r32/eax # Var-type
89/<- %eax 1/r32/ecx
c7 0/subop/copy *(eax+8) 0/imm32 # Var-block-depth
c7 0/subop/copy *(eax+0xc) 0/imm32 # Var-offset
c7 0/subop/copy *(eax+0x10) 0/imm32 # Var-register
$new-label:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-block: # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *Stmt-size) # => eax
(zero-out %eax *Stmt-size)
c7 0/subop/copy *eax 0/imm32/tag/block # Stmt-tag
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *(eax+4) 1/r32/ecx # Block-stmts
$new-block:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-var-def: # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *Stmt-size) # => eax
(zero-out %eax *Stmt-size)
c7 0/subop/copy *eax 2/imm32/tag/var-on-stack # Stmt-tag
# result->var = var
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *(eax+4) 1/r32/ecx # Vardef-var
$new-var-def:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
new-reg-var-def: # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
57/push-edi
# ecx = var
8b/-> *(ebp+0xc) 1/r32/ecx
# edi = result
(allocate *(ebp+8) *Stmt-size) # => eax
89/<- %edi 0/r32/eax
(zero-out %edi *Stmt-size)
# set tag
c7 0/subop/copy *edi 3/imm32/tag/var-in-register # Stmt-tag
# set output
(append-stmt-var Heap %ecx *(edi+0xc) 0) # Regvardef-outputs => eax
89/<- *(edi+0xc) 0/r32/eax # Regvardef-outputs
$new-reg-var-def:end:
89/<- %eax 7/r32/edi
# . restore registers
5f/pop-to-edi
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
append-list: # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *List-size) # => eax
(zero-out %eax *List-size)
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *eax 1/r32/ecx # List-value
# if (list == null) return result
81 7/subop/compare *(ebp+0x10) 0/imm32
74/jump-if-= $append-list:end/disp8
# otherwise append
# var curr/ecx = list
8b/-> *(ebp+0x10) 1/r32/ecx
# while (curr->next != null) curr = curr->next
{
81 7/subop/compare *(ecx+4) 0/imm32 # List-next
74/jump-if-= break/disp8
# curr = curr->next
8b/-> *(ecx+4) 1/r32/ecx
eb/jump loop/disp8
}
# curr->next = result
89/<- *(ecx+4) 0/r32/eax
# return list
8b/-> *(ebp+0x10) 0/r32/eax
$append-list:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
append-stmt-var: # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(allocate *(ebp+8) *Stmt-var-size) # => eax
(zero-out %eax *Stmt-var-size)
8b/-> *(ebp+0xc) 1/r32/ecx
89/<- *eax 1/r32/ecx # Stmt-var-value
8b/-> *(ebp+0x14) 1/r32/ecx
89/<- *(eax+8) 1/r32/ecx # Stmt-var-is-deref
# if (list == null) return result
81 7/subop/compare *(ebp+0x10) 0/imm32
74/jump-if-= $append-stmt-var:end/disp8
# otherwise append
# var curr/ecx: (handle stmt-var) = vars
8b/-> *(ebp+0x10) 1/r32/ecx
# while (curr->next != null) curr = curr->next
{
81 7/subop/compare *(ecx+4) 0/imm32 # List-next
74/jump-if-= break/disp8
# curr = curr->next
8b/-> *(ecx+4) 1/r32/ecx
eb/jump loop/disp8
}
# curr->next = result
89/<- *(ecx+4) 0/r32/eax
# return vars
8b/-> *(ebp+0x10) 0/r32/eax
$append-stmt-var:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
append-to-block: # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
56/push-esi
# esi = block
8b/-> *(ebp+0xc) 6/r32/esi
(append-list *(ebp+8) *(ebp+0x10) *(esi+4)) # ad, x, Block-stmts
89/<- *(esi+4) 0/r32/eax # Block-stmts
$append-to-block:end:
# . restore registers
5e/pop-to-esi
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
## Parsing types
# We need to create metadata on user-defined types, and we need to use this
# metadata as we parse instructions.
# However, we also want to allow types to be used before their definitions.
# This means we can't ever assume any type data structures exist.
lookup-or-create-constant: # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
56/push-esi
# var container-type/esi: type-id
(container-type *(ebp+8)) # => eax
#? (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
89/<- %esi 0/r32/eax
# var typeinfo/eax: (addr typeinfo)
(find-or-create-typeinfo %esi) # => eax
#? (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
# result = find-or-create-typeinfo-constant(typeinfo, field-name)
(find-or-create-typeinfo-constant %eax *(ebp+0xc)) # => eax
$lookup-or-create-constant:end:
# . restore registers
5e/pop-to-esi
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# container->var->type->right->left->value
container-type: # container: (handle stmt-var) -> result/eax: type-id
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
8b/-> *(ebp+8) 0/r32/eax
8b/-> *eax 0/r32/eax # Stmt-var-value
8b/-> *(eax+4) 0/r32/eax # Var-type
8b/-> *(eax+4) 0/r32/eax # Tree-right
8b/-> *eax 0/r32/eax # Tree-left
8b/-> *eax 0/r32/eax # Atom-value
$container-type:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-or-create-typeinfo: # t: type-id -> result/eax: (handle typeinfo)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# eax = find-typeinfo(t)
(find-typeinfo *(ebp+8)) # => eax
{
# if (curr != 0) break
3d/compare-eax-and 0/imm32
75/jump-if-!= break/disp8
$find-or-create-typeinfo:create:
(allocate Heap *Typeinfo-size) # => eax
(zero-out %eax *Typeinfo-size)
# result->id = t
8b/-> *(ebp+8) 1/r32/ecx
89/<- *eax 1/r32/ecx # Typeinfo-id
# result->fields = new table
50/push-eax
(new-stream Heap 0x40 0xc) # => eax
89/<- %ecx 0/r32/eax
58/pop-to-eax
89/<- *(eax+4) 1/r32/ecx
# result->next = Program->types
8b/-> *_Program-types 1/r32/ecx
89/<- *(eax+8) 1/r32/ecx # Typeinfo-next
# Program->types = result
89/<- *_Program-types 0/r32/eax
}
$find-or-create-typeinfo:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-typeinfo: # t: type-id -> result/eax: (handle typeinfo)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# ecx = t
8b/-> *(ebp+8) 1/r32/ecx
# var curr/eax: (handle typeinfo) = Program->types
8b/-> *_Program-types 0/r32/eax
{
# if (curr == 0) break
3d/compare-eax-and 0/imm32
74/jump-if-= break/disp8
# if (curr->id == t) return curr
39/compare *eax 1/r32/ecx # Typeinfo-id
0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
# curr = curr->next
8b/-> *(eax+8) 0/r32/eax
#
eb/jump loop/disp8
}
$find-typeinfo:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-or-create-typeinfo-constant: # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
56/push-esi
# esi = T->fields
8b/-> *(ebp+8) 6/r32/esi
8b/-> *(esi+4) 6/r32/esi # Typeinfo-fields
# esi = get-or-insert(T->fields, f)
(leaky-get-or-insert-slice %esi *(ebp+0xc) 0xc) # => eax
89/<- %esi 0/r32/eax
# if output var exists, return it
81 7/subop/compare *(esi+4) 0/imm32 # output var
8b/-> *(esi+4) 0/r32/eax # output var
75/jump-if-!= $find-or-create-typeinfo-constant:end/disp8
# var type/ecx: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
(allocate Heap *Tree-size) # => eax
c7 0/subop/copy *eax 6/imm32/constant # Atom-value
c7 0/subop/copy *(eax+4) 0/imm32 # Tree-right
(new-var Heap "field" %eax 0 -1 0) # => eax
# offset (constant value) isn't filled out yet
# save output var in row
89/<- *(esi+4) 0/r32/eax
$find-or-create-typeinfo-constant:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
populate-mu-type: # in: (addr stream byte), t: (handle typeinfo)
# pseudocode:
# var line: (stream byte 512)
# curr-offset = 0
# while true
# clear-stream(line)
# read-line-buffered(in, line)
# if line->write == 0
# abort
# word-slice = next-mu-token(line)
# if slice-empty?(word-slice) # end of line
# continue
# if slice-equal?(word-slice, "}")
# break
# var v: (handle var) = parse-var-with-type(word-slice, line)
# var r: (addr {(handle var) (handle var)}) = get-or-insert(t, v->name, row-size=12)
# TODO: ensure that r->first is null
# r->first = v
# if r->second == 0
# r->second = new var("dummy name", constant type, -1 offset)
# r->second->offset = curr-offset
# curr-offset += size-of(existing)
# TODO: ensure nothing else in line
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# edi = t
8b/-> *(ebp+0xc) 7/r32/edi
# var line/ecx: (stream byte 512)
81 5/subop/subtract %esp 0x200/imm32
68/push 0x200/imm32/length
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %ecx 4/r32/esp
# var word-slice/edx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %edx 4/r32/esp
# var curr-offset/ebx: int = 0
bb/copy-to-ebx 0/imm32
{
$populate-mu-type:line-loop:
(clear-stream %ecx)
(read-line-buffered *(ebp+8) %ecx)
# if (line->write == 0) abort
81 7/subop/compare *ecx 0/imm32
0f 84/jump-if-= $populate-mu-type:abort/disp32
#? # dump line {{{
#? (write 2 "parse-mu: ^")
#? (write-stream 2 %ecx)
#? (write 2 "$\n")
#? (rewind-stream %ecx)
#? # }}}
(next-mu-token %ecx %edx)
# if slice-empty?(word-slice) continue
(slice-empty? %edx)
3d/compare-eax-and 0/imm32
0f 85/jump-if-!= loop/disp32
# if slice-equal?(word-slice, "}") break
(slice-equal? %edx "}")
3d/compare-eax-and 0/imm32
0f 85/jump-if-!= break/disp32
# var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
(parse-var-with-type %edx %ecx) # => eax
89/<- %esi 0/r32/eax
# var r/eax: (addr {(handle var) (handle var)})
#? (write-buffered Stderr "populate-mu-type: typeinfo: ")
#? (print-int32-buffered Stderr %edi)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
(get-or-insert *(edi+4) *esi 0xc) # Typeinfo-fields Var-name => eax
# r->first = v
89/<- *eax 6/r32/esi
# if (r->second == 0) create a new var with some placeholder data
{
81 7/subop/compare *(eax+4) 0/imm32
75/jump-if-!= break/disp8
# temporarily spill r to esi
89/<- %esi 0/r32/eax
(new-literal Heap %edx) # => eax
89/<- *(esi+4) 0/r32/eax
89/<- %eax 6/r32/esi
#? (write-buffered Stderr "creating new output var ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
}
# r->second->offset = curr-offset
8b/-> *(eax+4) 0/r32/eax
#? (write-buffered Stderr "writing offset ")
#? (print-int32-buffered Stderr %ebx)
#? (write-buffered Stderr " to output var ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
89/<- *(eax+0xc) 3/r32/ebx
# curr-offset += size-of(v)
50/push-eax
(size-of %eax) # => eax
01/add-to %ebx 0/r32/eax
58/pop-to-eax
#
e9/jump loop/disp32
}
$populate-mu-type:end:
# . reclaim locals
81 0/subop/add %esp 0x214/imm32
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$populate-mu-type:abort:
# error("unexpected top-level command: " word-slice "\n")
(write-buffered Stderr "incomplete type definition '")
(type-name *edi) # Typeinfo-id => eax
(write-buffered Stderr %eax)
(write-buffered Stderr "\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
type-name: # index: int -> result/eax: (addr array byte)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(index Type-id *(ebp+8))
$type-name:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
index: # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
56/push-esi
# TODO: bounds-check index
# esi = arr
8b/-> *(ebp+8) 6/r32/esi
# eax = index
8b/-> *(ebp+0xc) 0/r32/eax
# eax = *(arr + 12 + index)
8b/-> *(esi+eax+0xc) 0/r32/eax
$index:end:
# . restore registers
5e/pop-to-esi
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
#######################################################
# Type-checking
#######################################################
check-mu-types:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
$check-mu-types:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
size-of: # v: (addr var) -> result/eax: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# if v is a literal, return 0
8b/-> *(ebp+8) 0/r32/eax
8b/-> *(eax+4) 0/r32/eax # Var-type
81 7/subop/compare *eax 0/imm32 # Tree-left
b8/copy-to-eax 0/imm32
74/jump-if-= $size-of:end/disp8
# hard-coded since we only support 'int' types for now
b8/copy-to-eax 4/imm32
$size-of:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
type-equal?: # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
# ecx = a
8b/-> *(ebp+8) 1/r32/ecx
# edx = b
8b/-> *(ebp+0xc) 2/r32/edx
# if (a == b) return true
8b/-> %ecx 0/r32/eax # Var-type
39/compare %edx 0/r32/eax # Var-type
b8/copy-to-eax 1/imm32/true
74/jump-if-= $type-equal?:end/disp8
# if (a < MAX_TYPE_ID) return false
81 7/subop/compare %ecx 0x10000/imm32
b8/copy-to-eax 0/imm32/false
72/jump-if-addr< $type-equal?:end/disp8
# if (b < MAX_TYPE_ID) return false
81 7/subop/compare %edx 0x10000/imm32
b8/copy-to-eax 0/imm32/false
72/jump-if-addr< $type-equal?:end/disp8
# if (!type-equal?(a->left, b->left)) return false
(type-equal? *ecx *edx) # Tree-left, Tree-left => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= $type-equal?:end/disp8
# return type-equal?(a->right, b->right)
(type-equal? *(ecx+4) *(edx+4)) # Tree-right, Tree-right => eax
$type-equal?:end:
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
== data
# not yet used, but it will be
Type-size: # (stream int)
0x18/imm32/write
0/imm32/read
0x100/imm32/length
# data
4/imm32 # literal
4/imm32 # int
4/imm32 # addr
0/imm32 # array (logic elsewhere)
8/imm32 # handle (fat pointer)
4/imm32 # boolean
0/imm32
0/imm32
# 0x20
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
== code
#######################################################
# Code-generation
#######################################################
emit-subx: # out: (addr buffered-file)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
57/push-edi
# edi = out
8b/-> *(ebp+8) 7/r32/edi
# var curr/ecx: (handle function) = *Program->functions
8b/-> *_Program-functions 1/r32/ecx
{
# if (curr == null) break
81 7/subop/compare %ecx 0/imm32
0f 84/jump-if-= break/disp32
(emit-subx-function %edi %ecx)
# curr = curr->next
8b/-> *(ecx+0x14) 1/r32/ecx # Function-next
e9/jump loop/disp32
}
$emit-subx:end:
# . restore registers
5f/pop-to-edi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-function: # out: (addr buffered-file), f: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
57/push-edi
# edi = out
8b/-> *(ebp+8) 7/r32/edi
# ecx = f
8b/-> *(ebp+0xc) 1/r32/ecx
# var vars/edx: (stack (addr var) 256)
81 5/subop/subtract %esp 0x400/imm32
68/push 0x400/imm32/length
68/push 0/imm32/top
89/<- %edx 4/r32/esp
#
(write-buffered %edi *ecx)
(write-buffered %edi ":\n")
# Important: each block's depth during code-generation should be identical
# to what it was during parsing.
c7 0/subop/copy *Curr-block-depth 1/imm32
(emit-subx-prologue %edi)
(emit-subx-block %edi *(ecx+0x10) %edx) # Function-body
(emit-subx-epilogue %edi)
$emit-subx-function:end:
# . reclaim locals
81 0/subop/add %esp 408/imm32
# . restore registers
5f/pop-to-edi
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-stmt-list: # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
# esi = stmts
8b/-> *(ebp+0xc) 6/r32/esi
# var var-seen?/edx: boolean <- copy false
ba/copy-to-edx 0/imm32/false
#
{
$emit-subx-stmt-list:loop:
81 7/subop/compare %esi 0/imm32
0f 84/jump-if-= break/disp32
# var curr-stmt/ecx = stmts->value
8b/-> *esi 1/r32/ecx # List-value
{
$emit-subx-stmt-list:check-for-block:
81 7/subop/compare *ecx 0/imm32/block # Stmt-tag
75/jump-if-!= break/disp8
$emit-subx-stmt-list:block:
(emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
}
{
$emit-subx-stmt-list:check-for-stmt:
81 7/subop/compare *ecx 1/imm32/stmt1 # Stmt-tag
0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:stmt1:
{
(is-mu-branch? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:branch-stmt:
# if !var-seen? break
81 7/subop/compare %edx 0/imm32/false
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:branch-stmt-and-var-seen:
# unconditional loops {{{
{
# if (!string-equal?(var->operation, "loop")) break
(string-equal? *(ecx+4) "loop") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:unconditional-loop:
81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts
# simple unconditional loops without a target
{
0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:zero-arg-unconditional-loop:
(emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "e9/jump loop/disp32")
(write-buffered *(ebp+8) Newline)
e9/jump $emit-subx-stmt-list:cleanup/disp32 # skip remaining statements; they're dead code
}
# unconditional loops with a target
{
0f 84/jump-if-= break/disp32
(emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
e9/jump $emit-subx-stmt-list:cleanup/disp32
}
}
# }}}
# unconditional breaks {{{
{
# if (!string-equal?(curr-stmt->operation, "break")) break
(string-equal? *(ecx+4) "break") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:unconditional-break:
81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts
# simple unconditional breaks without a target
0f 84/jump-if-= $emit-subx-stmt-list:emit-cleanup/disp32 # easy: just skip remaining statements
# unconditional breaks with a target
(emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
e9/jump $emit-subx-stmt-list:cleanup/disp32
}
# }}}
# simple conditional branches without a target {{{
81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts
{
0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:zero-arg-conditional-branch:
# var old-block-depth/eax: int = Curr-block-depth - 1
8b/-> *Curr-block-depth 3/r32/ebx
# cleanup prologue
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "{\n")
ff 0/subop/increment *Curr-block-depth
#
(emit-reverse-break *(ebp+8) %ecx)
# clean up until old block depth
(emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) %ebx)
# var target-block-depth/ebx: int = Curr-block-depth - 1
4b/decrement-ebx
# emit jump to target block
(string-starts-with? *(ecx+4) "break")
3d/compare-eax-and 0/imm32/false
{
74/jump-if-= break/disp8
(emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "break")
}
3d/compare-eax-and 0/imm32/false # just in case the function call modified flags
{
75/jump-if-!= break/disp8
(emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "loop")
}
# cleanup epilogue
ff 1/subop/decrement *Curr-block-depth
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "}\n")
# continue
e9/jump $emit-subx-stmt-list:continue/disp32
}
# }}}
# conditional branches with an explicit target {{{
{
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:conditional-branch-with-target:
# cleanup prologue
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "{\n")
ff 0/subop/increment *Curr-block-depth
#
(emit-reverse-break *(ebp+8) %ecx)
(emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
# cleanup epilogue
ff 1/subop/decrement *Curr-block-depth
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "}\n")
# continue
e9/jump $emit-subx-stmt-list:continue/disp32
}
# }}}
}
$emit-subx-stmt-list:1-to-1:
(emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
}
{
$emit-subx-stmt-list:check-for-var-def:
81 7/subop/compare *ecx 2/imm32/var-def # Stmt-tag
75/jump-if-!= break/disp8
$emit-subx-stmt-list:var-def:
(emit-subx-var-def *(ebp+8) %ecx)
(push *(ebp+0x10) *(ecx+4)) # Vardef-var
# var-seen? = true
ba/copy-to-edx 1/imm32/true
}
{
$emit-subx-stmt-list:check-for-reg-var-def:
81 7/subop/compare *ecx 3/imm32/reg-var-def # Stmt-tag
0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:reg-var-def:
# TODO: ensure that there's exactly one output
# var output/eax: (handle var) = curr-stmt->outputs->value
8b/-> *(ecx+0xc) 0/r32/eax
8b/-> *eax 0/r32/eax
# ensure that output is in a register
81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register
0f 84/jump-if-= $emit-subx-stmt-list:abort-reg-var-def-without-register/disp32
# emit spill
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "ff 6/subop/push %")
(write-buffered *(ebp+8) *(eax+0x10))
(write-buffered *(ebp+8) Newline)
# register variable definition
(push *(ebp+0x10) %eax)
# emit the instruction as usual
(emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
# var-seen? = true
ba/copy-to-edx 1/imm32/true
}
$emit-subx-stmt-list:continue:
# TODO: raise an error on unrecognized Stmt-tag
8b/-> *(esi+4) 6/r32/esi # List-next
e9/jump loop/disp32
}
$emit-subx-stmt-list:emit-cleanup:
(emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
$emit-subx-stmt-list:cleanup:
(clean-up-blocks *(ebp+0x10) *Curr-block-depth)
$emit-subx-stmt-list:end:
# . restore registers
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$emit-subx-stmt-list:abort-reg-var-def-without-register:
# error("var '" var->name "' initialized from an instruction must live in a register\n")
(write-buffered Stderr "var '")
(write-buffered Stderr *eax) # Var-name
(write-buffered Stderr "' initialized from an instruction must live in a register\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
emit-subx-cleanup-and-unconditional-nonlocal-branch: # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
# ecx = stmt
8b/-> *(ebp+0xc) 1/r32/ecx
# var target/edx: (addr array byte) = curr-stmt->inouts->value->name
8b/-> *(ecx+8) 2/r32/edx # Stmt1-inouts
8b/-> *edx 2/r32/edx # Stmt-var-value
8b/-> *edx 2/r32/edx # Var-name
# clean up until target block
(emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
# emit jump to target block
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "e9/jump ")
(write-buffered *(ebp+8) %edx)
(string-starts-with? *(ecx+4) "break")
3d/compare-eax-and 0/imm32/false
{
74/jump-if-= break/disp8
(write-buffered *(ebp+8) ":break/disp32\n")
}
3d/compare-eax-and 0/imm32/false # just in case the function call modified flags
{
75/jump-if-!= break/disp8
(write-buffered *(ebp+8) ":loop/disp32\n")
}
$emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
is-mu-branch?: # stmt: (addr stmt1) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# ecx = stmt
8b/-> *(ebp+8) 1/r32/ecx
# if (stmt->operation starts with "loop") return true
(string-starts-with? *(ecx+4) "loop") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-not-equal $is-mu-branch?:end/disp8
# otherwise return (stmt->operation starts with "break")
(string-starts-with? *(ecx+4) "break") # Stmt1-operation => eax
$is-mu-branch?:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-reverse-break: # out: (addr buffered-file), stmt: (addr stmt1)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
# eax = stmt
8b/-> *(ebp+0xc) 0/r32/eax
#
(get Reverse-branch *(eax+4) 8 "reverse-branch: ") # Stmt1-operation => eax: (addr addr array byte)
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) *eax)
(write-buffered *(ebp+8) " break/disp32\n")
$emit-reverse-break:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
== data
Reverse-branch: # (table string string)
# a table is a stream
0xa0/imm32/write
0/imm32/read
0xa0/imm32/length
# data
"break-if-="/imm32 "0f 85/jump-if-!="/imm32
"loop-if-="/imm32 "0f 85/jump-if-!="/imm32
"break-if-!="/imm32 "0f 84/jump-if-="/imm32
"loop-if-!="/imm32 "0f 84/jump-if-="/imm32
"break-if-<"/imm32 "0f 8d/jump-if->="/imm32
"loop-if-<"/imm32 "0f 8d/jump-if->="/imm32
"break-if->"/imm32 "0f 8e/jump-if-<="/imm32
"loop-if->"/imm32 "0f 8e/jump-if-<="/imm32
"break-if-<="/imm32 "0f 87/jump-if->"/imm32
"loop-if-<="/imm32 "0f 87/jump-if->"/imm32
"break-if->="/imm32 "0f 8c/jump-if-<"/imm32
"loop-if->="/imm32 "0f 8c/jump-if-<"/imm32
"break-if-addr<"/imm32 "0f 83/jump-if-addr>="/imm32
"loop-if-addr<"/imm32 "0f 83/jump-if-addr>="/imm32
"break-if-addr>"/imm32 "0f 86/jump-if-addr<="/imm32
"loop-if-addr>"/imm32 "0f 86/jump-if-addr<="/imm32
"break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
"loop-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
"break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
"loop-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
== code
emit-unconditional-jump-to-depth: # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
# ecx = vars
8b/-> *(ebp+0xc) 1/r32/ecx
# var eax: int = vars->top
8b/-> *ecx 0/r32/eax
# var min/ecx: (address (handle var)) = vars->data
81 0/subop/add %ecx 8/imm32
# var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
81 5/subop/subtract %eax 4/imm32
8d/copy-address *(ecx+eax) 0/r32/eax
# edx = depth
8b/-> *(ebp+0x10) 2/r32/edx
{
$emit-unconditional-jump-to-depth:loop:
# if (curr < min) break
39/compare %eax 1/r32/ecx
0f 82/jump-if-addr< break/disp32
# var v/ebx: (handle var) = *curr
8b/-> *eax 3/r32/ebx
# if (v->block-depth < until-block-depth) break
39/compare *(ebx+8) 2/r32/edx # Var-block-depth
0f 8c/jump-if-< break/disp32
{
$emit-unconditional-jump-to-depth:check:
# if v->block-depth != until-block-depth, continue
39/compare *(ebx+8) 2/r32/edx # Var-block-depth
0f 85/jump-if-!= break/disp32
$emit-unconditional-jump-to-depth:depth-found:
# if v is not a literal, continue
# . var eax: int = size-of(v)
50/push-eax
(size-of %ebx) # => eax
# . if (eax != 0) continue
3d/compare-eax-and 0/imm32
58/pop-to-eax
#
0f 85/jump-if-!= break/disp32
$emit-unconditional-jump-to-depth:label-found:
# emit unconditional jump, then return
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "e9/jump ")
(write-buffered *(ebp+8) *ebx) # Var-name
(write-buffered *(ebp+8) ":")
(write-buffered *(ebp+8) *(ebp+0x14))
(write-buffered *(ebp+8) "/disp32\n")
eb/jump $emit-unconditional-jump-to-depth:end/disp8
}
# curr -= 4
2d/subtract-from-eax 4/imm32
e9/jump loop/disp32
}
# TODO: error if no label at 'depth' was found
$emit-unconditional-jump-to-depth:end:
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# emit clean-up code for 'vars' until some block depth
# doesn't actually modify 'vars' so we need traverse manually inside the stack
emit-cleanup-code-until-depth: # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
# ecx = vars
8b/-> *(ebp+0xc) 1/r32/ecx
# var eax: int = vars->top
8b/-> *ecx 0/r32/eax
# var min/ecx: (address (handle var)) = vars->data
81 0/subop/add %ecx 8/imm32
# var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
81 5/subop/subtract %eax 4/imm32
8d/copy-address *(ecx+eax) 0/r32/eax
# edx = until-block-depth
8b/-> *(ebp+0x10) 2/r32/edx
{
$emit-cleanup-code-until-depth:loop:
# if (curr < min) break
39/compare %eax 1/r32/ecx
0f 82/jump-if-addr< break/disp32
# var v/ebx: (handle var) = *curr
8b/-> *eax 3/r32/ebx
# if (v->block-depth < until-block-depth) break
39/compare *(ebx+8) 2/r32/edx # Var-block-depth
0f 8c/jump-if-< break/disp32
# if v is in a register
81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register
{
74/jump-if-= break/disp8
$emit-cleanup-code-until-depth:reclaim-var-in-register:
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "8f 0/subop/pop %")
(write-buffered *(ebp+8) *(ebx+0x10))
(write-buffered *(ebp+8) Newline)
}
# otherwise v is on the stack
{
75/jump-if-!= break/disp8
$emit-cleanup-code-until-depth:reclaim-var-on-stack:
50/push-eax
(size-of %ebx) # => eax
# don't emit code for labels
3d/compare-eax-and 0/imm32
74/jump-if-= break/disp8
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "81 0/subop/add %esp ")
(print-int32-buffered *(ebp+8) %eax)
(write-buffered *(ebp+8) "/imm32\n")
58/pop-to-eax
}
# curr -= 4
2d/subtract-from-eax 4/imm32
e9/jump loop/disp32
}
$emit-cleanup-code-until-depth:end:
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# emit clean-up code for 'vars' until a given label is encountered
# doesn't actually modify 'vars' so we need traverse manually inside the stack
emit-cleanup-code-until-target: # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
# ecx = vars
8b/-> *(ebp+0xc) 1/r32/ecx
# var eax: int = vars->top
8b/-> *ecx 0/r32/eax
# var min/ecx: (address (handle var)) = vars->data
81 0/subop/add %ecx 8/imm32
# var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
81 5/subop/subtract %eax 4/imm32
8d/copy-address *(ecx+eax) 2/r32/edx
{
$emit-cleanup-code-until-target:loop:
# if (curr < min) break
39/compare %edx 1/r32/ecx
0f 82/jump-if-addr< break/disp32
# var v/ebx: (handle var) = *curr
8b/-> *edx 3/r32/ebx
# if (v->name == until-block-label) break
(string-equal? *ebx *(ebp+0x10)) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= break/disp32
# if v is in a register
81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register
{
74/jump-if-= break/disp8
$emit-cleanup-code-until-target:reclaim-var-in-register:
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "8f 0/subop/pop %")
(write-buffered *(ebp+8) *(ebx+0x10))
(write-buffered *(ebp+8) Newline)
}
# otherwise v is on the stack
{
75/jump-if-!= break/disp8
$emit-cleanup-code-until-target:reclaim-var-on-stack:
(size-of %ebx) # => eax
# don't emit code for labels
3d/compare-eax-and 0/imm32
74/jump-if-= break/disp8
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "81 0/subop/add %esp ")
(print-int32-buffered *(ebp+8) %eax)
(write-buffered *(ebp+8) "/imm32\n")
}
# curr -= 4
81 5/subop/subtract %edx 4/imm32
e9/jump loop/disp32
}
$emit-cleanup-code-until-target:end:
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# clean up global state for 'vars' until some block depth
clean-up-blocks: # vars: (addr stack (handle var)), until-block-depth: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
# esi = vars
8b/-> *(ebp+8) 6/r32/esi
# ecx = until-block-depth
8b/-> *(ebp+0xc) 1/r32/ecx
{
$clean-up-blocks:reclaim-loop:
# if (vars->top <= 0) break
81 7/subop/compare *esi 0/imm32 # Stack-top
7e/jump-if-<= break/disp8
# var v/eax: (handle var) = top(vars)
(top %esi) # => eax
# if (v->block-depth < until-block-depth) break
39/compare *(eax+8) 1/r32/ecx # Var-block-depth
7c/jump-if-< break/disp8
# if v is on the stack, update Next-local-stack-offset
81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register
{
75/jump-if-!= break/disp8
$clean-up-blocks:reclaim-var-on-stack:
(size-of %eax) # => eax
01/add *Next-local-stack-offset 0/r32/eax
}
(pop %esi)
e9/jump loop/disp32
}
$clean-up-blocks:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-var-def: # out: (addr buffered-file), stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# eax = stmt
8b/-> *(ebp+0xc) 0/r32/eax
# var n/eax: int = size-of(stmt->var)
(size-of *(eax+4)) # Vardef-var => eax
# while n > 0
{
3d/compare-eax-with 0/imm32
7e/jump-if-<= break/disp8
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "68/push 0/imm32\n")
# n -= 4
2d/subtract-from-eax 4/imm32
#
eb/jump loop/disp8
}
$emit-subx-var-def:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-stmt: # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive), functions: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# handle some special cases
# ecx = stmt
8b/-> *(ebp+0xc) 1/r32/ecx
# array length {{{
{
# if (!string-equal?(stmt->operation, "length")) break
(string-equal? *(ecx+4) "length") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:array-length:
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "8b/copy-from *")
# inouts[0]->register
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
8b/-> *eax 0/r32/eax # Stmt-var-value
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
#
(write-buffered *(ebp+8) " ")
# outputs[0] "/r32"
8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs
8b/-> *eax 0/r32/eax # Stmt-var-value
(get Registers *(eax+0x10) 8 "Registers") # Var-register => eax
(print-int32-buffered *(ebp+8) *eax)
(write-buffered *(ebp+8) "/r32\n")
e9/jump $emit-subx-stmt:end/disp32
}
# }}}
# index into array {{{
# TODO: support literal index
{
# if (!string-equal?(var->operation, "index")) break
(string-equal? *(ecx+4) "index") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:index:
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "8d/copy-address *(")
# inouts[0]->register " + "
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts
8b/-> *eax 0/r32/eax # List-value
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
#
(write-buffered *(ebp+8) " + ")
# inouts[1]->register
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts
8b/-> *(eax+4) 0/r32/eax # Stmt-var-next
8b/-> *eax 0/r32/eax # Stmt-var-value
# TODO: handle Stmt-var-is-deref
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
#
(write-buffered *(ebp+8) "<<2 + 4) ")
# outputs[0] "/r32"
8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs
8b/-> *eax 0/r32/eax # List-value
(get Registers *(eax+0x10) 8 "Registers") # Var-register => eax
(print-int32-buffered *(ebp+8) *eax)
(write-buffered *(ebp+8) "/r32\n")
e9/jump $emit-subx-stmt:end/disp32
}
# }}}
# get field from record {{{
{
# if (!string-equal?(var->operation, "get")) break
(string-equal? *(ecx+4) "get") # Stmt1-operation => eax
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:get:
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "8d/copy-address *(")
# inouts[0]->register " + "
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts
8b/-> *eax 0/r32/eax # Stmt-var-value
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
#
(write-buffered *(ebp+8) " + ")
(print-mu-get-offset *(ebp+8) %ecx)
(write-buffered *(ebp+8) ") ")
# outputs[0] "/r32"
8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs
8b/-> *eax 0/r32/eax # List-value
(get Registers *(eax+0x10) 8 "Registers") # Var-register => eax
(print-int32-buffered *(ebp+8) *eax)
(write-buffered *(ebp+8) "/r32\n")
e9/jump $emit-subx-stmt:end/disp32
}
# }}}
# if stmt matches a primitive, emit it
{
$emit-subx-stmt:check-for-primitive:
(find-matching-primitive *(ebp+0x10) *(ebp+0xc)) # primitives, stmt => curr/eax
3d/compare-eax-and 0/imm32
74/jump-if-= break/disp8
$emit-subx-stmt:primitive:
(emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax) # out, stmt, curr
e9/jump $emit-subx-stmt:end/disp32
}
# else if stmt matches a function, emit a call to it
{
$emit-subx-stmt:check-for-call:
(find-matching-function *(ebp+0x14) *(ebp+0xc)) # functions, stmt => curr/eax
3d/compare-eax-and 0/imm32
74/jump-if-= break/disp8
$emit-subx-stmt:call:
(emit-subx-call *(ebp+8) *(ebp+0xc) %eax) # out, stmt, curr
e9/jump $emit-subx-stmt:end/disp32
}
# else assume it's a SubX function (TODO: how to type-check?!)
(emit-hailmary-call *(ebp+8) *(ebp+0xc))
$emit-subx-stmt:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$emit-subx-stmt:abort:
# error("couldn't translate '" stmt "'\n")
(write-buffered Stderr "couldn't translate an instruction with operation '")
8b/-> *(ebp+0xc) 0/r32/eax
(write-buffered Stderr *(eax+4)) # Stmt1-operation
(write-buffered Stderr "'\n")
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
print-mu-get-offset: # out: (addr buffered-file), stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
# var second-inout/eax: (handle stmt-var) = stmt->inouts->next
8b/-> *(ebp+0xc) 0/r32/eax
8b/-> *(eax+8) 0/r32/eax # Stmt1-inouts
8b/-> *(eax+4) 0/r32/eax # Stmt-var-next
# var output-var/eax: (handle var) = second-inout->value
8b/-> *eax 0/r32/eax # Stmt-var-value
# print offset
#? (write-buffered Stderr "emitting offset from output var ")
#? (print-int32-buffered Stderr %eax)
#? (write-buffered Stderr Newline)
#? (flush Stderr)
(print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-offset
$emit-get-offset:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-block: # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
# esi = block
8b/-> *(ebp+0xc) 6/r32/esi
# var stmts/eax: (handle list stmt) = block->statements
8b/-> *(esi+4) 0/r32/eax # Block-stmts
#
{
$emit-subx-block:check-empty:
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "{\n")
# var v/ecx: (addr array byte) = block->var->name
8b/-> *(esi+8) 1/r32/ecx # Block-var
(write-buffered *(ebp+8) *ecx) # Var-name
(write-buffered *(ebp+8) ":loop:\n")
ff 0/subop/increment *Curr-block-depth
(push *(ebp+0x10) %ecx)
(emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
(pop *(ebp+0x10)) # => eax
ff 1/subop/decrement *Curr-block-depth
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "}\n")
(write-buffered *(ebp+8) *ecx) # Var-name
(write-buffered *(ebp+8) ":break:\n")
}
$emit-subx-block:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# Primitives supported
# For each operation, put variants with hard-coded registers before flexible ones.
== data
Primitives:
# - increment/decrement
_Primitive-inc-eax:
# var/eax <- increment => 40/increment-eax
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-eax/imm32/outputs
"40/increment-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-ecx/imm32/next
_Primitive-inc-ecx:
# var/ecx <- increment => 41/increment-ecx
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-ecx/imm32/outputs
"41/increment-ecx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-edx/imm32/next
_Primitive-inc-edx:
# var/edx <- increment => 42/increment-edx
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-edx/imm32/outputs
"42/increment-edx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-ebx/imm32/next
_Primitive-inc-ebx:
# var/ebx <- increment => 43/increment-ebx
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-ebx/imm32/outputs
"43/increment-ebx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-esi/imm32/next
_Primitive-inc-esi:
# var/esi <- increment => 46/increment-esi
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-esi/imm32/outputs
"46/increment-esi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-edi/imm32/next
_Primitive-inc-edi:
# var/edi <- increment => 47/increment-edi
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-edi/imm32/outputs
"47/increment-edi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-eax/imm32/next
_Primitive-dec-eax:
# var/eax <- decrement => 48/decrement-eax
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-eax/imm32/outputs
"48/decrement-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-ecx/imm32/next
_Primitive-dec-ecx:
# var/ecx <- decrement => 49/decrement-ecx
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-ecx/imm32/outputs
"49/decrement-ecx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-edx/imm32/next
_Primitive-dec-edx:
# var/edx <- decrement => 4a/decrement-edx
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-edx/imm32/outputs
"4a/decrement-edx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-ebx/imm32/next
_Primitive-dec-ebx:
# var/ebx <- decrement => 4b/decrement-ebx
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-ebx/imm32/outputs
"4b/decrement-ebx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-esi/imm32/next
_Primitive-dec-esi:
# var/esi <- decrement => 4e/decrement-esi
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-esi/imm32/outputs
"4e/decrement-esi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-edi/imm32/next
_Primitive-dec-edi:
# var/edi <- decrement => 4f/decrement-edi
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-edi/imm32/outputs
"4f/decrement-edi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-mem/imm32/next
_Primitive-inc-mem:
# increment var => ff 0/subop/increment *(ebp+__)
"increment"/imm32/name
Single-int-var-in-mem/imm32/inouts
0/imm32/no-outputs
"ff 0/subop/increment"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-inc-reg/imm32/next
_Primitive-inc-reg:
# var/reg <- increment => ff 0/subop/increment %__
"increment"/imm32/name
0/imm32/no-inouts
Single-int-var-in-some-register/imm32/outputs
"ff 0/subop/increment"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-mem/imm32/next
_Primitive-dec-mem:
# decrement var => ff 1/subop/decrement *(ebp+__)
"decrement"/imm32/name
Single-int-var-in-mem/imm32/inouts
0/imm32/no-outputs
"ff 1/subop/decrement"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-dec-reg/imm32/next
_Primitive-dec-reg:
# var/reg <- decrement => ff 1/subop/decrement %__
"decrement"/imm32/name
0/imm32/no-inouts
Single-int-var-in-some-register/imm32/outputs
"ff 1/subop/decrement"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-to-eax/imm32/next
# - add
_Primitive-add-to-eax:
# var/eax <- add lit => 05/add-to-eax lit/imm32
"add"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"05/add-to-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-reg-to-reg/imm32/next
_Primitive-add-reg-to-reg:
# var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
"add"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"01/add-to"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-reg-to-mem/imm32/next
_Primitive-add-reg-to-mem:
# add-to var1 var2/reg => 01/add-to var1 var2/r32
"add-to"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"01/add-to"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-mem-to-reg/imm32/next
_Primitive-add-mem-to-reg:
# var1/reg <- add var2 => 03/add var2/rm32 var1/r32
"add"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"03/add"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-lit-to-reg/imm32/next
_Primitive-add-lit-to-reg:
# var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
"add"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 0/subop/add"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-add-lit-to-mem/imm32/next
_Primitive-add-lit-to-mem:
# add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
"add-to"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 0/subop/add"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-second-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-from-eax/imm32/next
# - subtract
_Primitive-subtract-from-eax:
# var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
"subtract"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"2d/subtract-from-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-reg-from-reg/imm32/next
_Primitive-subtract-reg-from-reg:
# var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
"subtract"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"29/subtract-from"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-reg-from-mem/imm32/next
_Primitive-subtract-reg-from-mem:
# subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
"subtract-from"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"29/subtract-from"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-mem-from-reg/imm32/next
_Primitive-subtract-mem-from-reg:
# var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
"subtract"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"2b/subtract"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-lit-from-reg/imm32/next
_Primitive-subtract-lit-from-reg:
# var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
"subtract"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 5/subop/subtract"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-subtract-lit-from-mem/imm32/next
_Primitive-subtract-lit-from-mem:
# subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
"subtract-from"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 5/subop/subtract"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-with-eax/imm32/next
# - and
_Primitive-and-with-eax:
# var/eax <- and lit => 25/and-with-eax lit/imm32
"and"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"25/and-with-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-reg-with-reg/imm32/next
_Primitive-and-reg-with-reg:
# var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
"and"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"21/and-with"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-reg-with-mem/imm32/next
_Primitive-and-reg-with-mem:
# and-with var1 var2/reg => 21/and-with var1 var2/r32
"and-with"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"21/and-with"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-mem-with-reg/imm32/next
_Primitive-and-mem-with-reg:
# var1/reg <- and var2 => 23/and var2/rm32 var1/r32
"and"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"23/and"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-lit-with-reg/imm32/next
_Primitive-and-lit-with-reg:
# var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
"and"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 4/subop/and"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-and-lit-with-mem/imm32/next
_Primitive-and-lit-with-mem:
# and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
"and-with"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 4/subop/and"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-with-eax/imm32/next
# - or
_Primitive-or-with-eax:
# var/eax <- or lit => 0d/or-with-eax lit/imm32
"or"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"0d/or-with-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-reg-with-reg/imm32/next
_Primitive-or-reg-with-reg:
# var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
"or"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"09/or-with"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-reg-with-mem/imm32/next
_Primitive-or-reg-with-mem:
# or-with var1 var2/reg => 09/or-with var1 var2/r32
"or-with"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"09/or-with"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-mem-with-reg/imm32/next
_Primitive-or-mem-with-reg:
# var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
"or"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"0b/or"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-lit-with-reg/imm32/next
_Primitive-or-lit-with-reg:
# var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
"or"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 1/subop/or"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-or-lit-with-mem/imm32/next
_Primitive-or-lit-with-mem:
# or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
"or-with"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 1/subop/or"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-second-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-with-eax/imm32/next
# - xor
_Primitive-xor-with-eax:
# var/eax <- xor lit => 35/xor-with-eax lit/imm32
"xor"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"35/xor-with-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-reg-with-reg/imm32/next
_Primitive-xor-reg-with-reg:
# var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
"xor"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"31/xor-with"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-reg-with-mem/imm32/next
_Primitive-xor-reg-with-mem:
# xor-with var1 var2/reg => 31/xor-with var1 var2/r32
"xor-with"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"31/xor-with"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-mem-with-reg/imm32/next
_Primitive-xor-mem-with-reg:
# var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
"xor"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"33/xor"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-lit-with-reg/imm32/next
_Primitive-xor-lit-with-reg:
# var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
"xor"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 6/subop/xor"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-xor-lit-with-mem/imm32/next
_Primitive-xor-lit-with-mem:
# xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
"xor-with"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 6/subop/xor"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-first-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-copy-to-eax/imm32/next
# - copy
_Primitive-copy-to-eax:
# var/eax <- copy lit => b8/copy-to-eax lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-eax/imm32/outputs
"b8/copy-to-eax"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-to-ecx/imm32/next
_Primitive-copy-to-ecx:
# var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-ecx/imm32/outputs
"b9/copy-to-ecx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-to-edx/imm32/next
_Primitive-copy-to-edx:
# var/edx <- copy lit => ba/copy-to-edx lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-edx/imm32/outputs
"ba/copy-to-edx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-to-ebx/imm32/next
_Primitive-copy-to-ebx:
# var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-ebx/imm32/outputs
"bb/copy-to-ebx"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-to-esi/imm32/next
_Primitive-copy-to-esi:
# var/esi <- copy lit => be/copy-to-esi lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-esi/imm32/outputs
"be/copy-to-esi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-to-edi/imm32/next
_Primitive-copy-to-edi:
# var/edi <- copy lit => bf/copy-to-edi lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-edi/imm32/outputs
"bf/copy-to-edi"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-reg-to-reg/imm32/next
_Primitive-copy-reg-to-reg:
# var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
"copy"/imm32/name
Single-int-var-in-some-register/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"89/copy-to"/imm32/subx-name
3/imm32/rm32-is-first-output
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-reg-to-mem/imm32/next
_Primitive-copy-reg-to-mem:
# copy-to var1 var2/reg => 89/copy-to var1 var2/r32
"copy-to"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"89/copy-to"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-mem-to-reg/imm32/next
_Primitive-copy-mem-to-reg:
# var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
"copy"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"8b/copy-from"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-lit-to-reg/imm32/next
_Primitive-copy-lit-to-reg:
# var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
"copy"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"c7 0/subop/copy"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-copy-lit-to-mem/imm32/next
_Primitive-copy-lit-to-mem:
# copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
"copy-to"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"c7 0/subop/copy"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-first-inout
0/imm32/no-disp32
1/imm32/output-is-write-only
_Primitive-compare-mem-with-reg/imm32/next
# - compare
_Primitive-compare-mem-with-reg:
# compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
"compare"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"39/compare->"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-compare-reg-with-mem/imm32/next
_Primitive-compare-reg-with-mem:
# compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
"compare"/imm32/name
Two-args-int-reg-int-stack/imm32/inouts
0/imm32/outputs
"3b/compare<-"/imm32/subx-name
2/imm32/rm32-is-second-inout
1/imm32/r32-is-first-inout
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-compare-eax-with-literal/imm32/next
_Primitive-compare-eax-with-literal:
# compare var1/eax n => 3d/compare-eax-with n/imm32
"compare"/imm32/name
Two-args-int-eax-int-literal/imm32/inouts
0/imm32/outputs
"3d/compare-eax-with"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
2/imm32/imm32-is-second-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-compare-reg-with-literal/imm32/next
_Primitive-compare-reg-with-literal:
# compare var1/reg n => 81 7/subop/compare %reg n/imm32
"compare"/imm32/name
Int-var-in-register-and-literal/imm32/inouts
0/imm32/outputs
"81 7/subop/compare"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-second-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-compare-mem-with-literal/imm32/next
_Primitive-compare-mem-with-literal:
# compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
"compare"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 7/subop/compare"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-second-inout
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-multiply-reg-by-mem/imm32/next
# - multiply
_Primitive-multiply-reg-by-mem:
# var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
"multiply"/imm32/name
Single-int-var-in-mem/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"0f af/multiply"/imm32/subx-name
1/imm32/rm32-is-first-inout
3/imm32/r32-is-first-output
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/output-is-write-only
_Primitive-break-if-addr</imm32/next
# - branches
_Primitive-break-if-addr<:
"break-if-addr<"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 82/jump-if-addr< break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-addr>=/imm32/next
_Primitive-break-if-addr>=:
"break-if-addr>="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-=/imm32/next
_Primitive-break-if-=:
"break-if-="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 84/jump-if-= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-!=/imm32/next
_Primitive-break-if-!=:
"break-if-!="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 85/jump-if-!= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-addr<=/imm32/next
_Primitive-break-if-addr<=:
"break-if-addr<="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-addr>/imm32/next
_Primitive-break-if-addr>:
"break-if-addr>"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 87/jump-if-addr> break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-</imm32/next
_Primitive-break-if-<:
"break-if-<"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8c/jump-if-< break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if->=/imm32/next
_Primitive-break-if->=:
"break-if->="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8d/jump-if->= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-<=/imm32/next
_Primitive-break-if-<=:
"break-if-<="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8e/jump-if-<= break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if->/imm32/next
_Primitive-break-if->:
"break-if->"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8f/jump-if-> break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break/imm32/next
_Primitive-break:
"break"/imm32/name
0/imm32/inouts
0/imm32/outputs
"e9/jump break/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-addr</imm32/next
_Primitive-loop-if-addr<:
"loop-if-addr<"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-addr>=/imm32/next
_Primitive-loop-if-addr>=:
"loop-if-addr>="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-=/imm32/next
_Primitive-loop-if-=:
"loop-if-="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 84/jump-if-= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-!=/imm32/next
_Primitive-loop-if-!=:
"loop-if-!="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 85/jump-if-!= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-addr<=/imm32/next
_Primitive-loop-if-addr<=:
"loop-if-addr<="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-addr>/imm32/next
_Primitive-loop-if-addr>:
"loop-if-addr>"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-</imm32/next
_Primitive-loop-if-<:
"loop-if-<"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8c/jump-if-< loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if->=/imm32/next
_Primitive-loop-if->=:
"loop-if->="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8d/jump-if->= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if-<=/imm32/next
_Primitive-loop-if-<=:
"loop-if-<="/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop-if->/imm32/next
_Primitive-loop-if->:
"loop-if->"/imm32/name
0/imm32/inouts
0/imm32/outputs
"0f 8f/jump-if-> loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-loop/imm32/next # we probably don't need an unconditional break
_Primitive-loop:
"loop"/imm32/name
0/imm32/inouts
0/imm32/outputs
"e9/jump loop/disp32"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
0/imm32/no-disp32
0/imm32/no-output
_Primitive-break-if-addr<-named/imm32/next
# - branches to named blocks
_Primitive-break-if-addr<-named:
"break-if-addr<"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 82/jump-if-addr<"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-addr>=-named/imm32/next
_Primitive-break-if-addr>=-named:
"break-if-addr>="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 83/jump-if-addr>="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-=-named/imm32/next
_Primitive-break-if-=-named:
"break-if-="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 84/jump-if-="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-!=-named/imm32/next
_Primitive-break-if-!=-named:
"break-if-!="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 85/jump-if-!="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-addr<=-named/imm32/next
_Primitive-break-if-addr<=-named:
"break-if-addr<="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 86/jump-if-addr<="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-addr>-named/imm32/next
_Primitive-break-if-addr>-named:
"break-if-addr>"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 87/jump-if-addr>"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-<-named/imm32/next
_Primitive-break-if-<-named:
"break-if-<"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8c/jump-if-<"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if->=-named/imm32/next
_Primitive-break-if->=-named:
"break-if->="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8d/jump-if->="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if-<=-named/imm32/next
_Primitive-break-if-<=-named:
"break-if-<="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8e/jump-if-<="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-if->-named/imm32/next
_Primitive-break-if->-named:
"break-if->"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8f/jump-if->"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-break-named/imm32/next
_Primitive-break-named:
"break"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"e9/jump"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-addr<-named/imm32/next
_Primitive-loop-if-addr<-named:
"loop-if-addr<"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 82/jump-if-addr<"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-addr>=-named/imm32/next
_Primitive-loop-if-addr>=-named:
"loop-if-addr>="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 83/jump-if-addr>="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-=-named/imm32/next
_Primitive-loop-if-=-named:
"loop-if-="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 84/jump-if-="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-!=-named/imm32/next
_Primitive-loop-if-!=-named:
"loop-if-!="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 85/jump-if-!="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-addr<=-named/imm32/next
_Primitive-loop-if-addr<=-named:
"loop-if-addr<="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 86/jump-if-addr<="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-addr>-named/imm32/next
_Primitive-loop-if-addr>-named:
"loop-if-addr>"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 87/jump-if-addr>"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-<-named/imm32/next
_Primitive-loop-if-<-named:
"loop-if-<"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8c/jump-if-<"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if->=-named/imm32/next
_Primitive-loop-if->=-named:
"loop-if->="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8d/jump-if->="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if-<=-named/imm32/next
_Primitive-loop-if-<=-named:
"loop-if-<="/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8e/jump-if-<="/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-if->-named/imm32/next
_Primitive-loop-if->-named:
"loop-if->"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"0f 8f/jump-if->"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
_Primitive-loop-named/imm32/next # we probably don't need an unconditional break
_Primitive-loop-named:
"loop"/imm32/name
Single-lit-var/imm32/inouts
0/imm32/outputs
"e9/jump"/imm32/subx-name
0/imm32/no-rm32
0/imm32/no-r32
0/imm32/no-imm32
1/imm32/disp32-is-first-inout
0/imm32/no-output
0/imm32/next
Single-int-var-in-mem:
Int-var-in-mem/imm32
0/imm32/next
Int-var-in-mem:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
1/imm32/some-stack-offset
0/imm32/no-register
Two-args-int-stack-int-reg:
Int-var-in-mem/imm32
Single-int-var-in-some-register/imm32/next
Two-args-int-reg-int-stack:
Int-var-in-some-register/imm32
Single-int-var-in-mem/imm32/next
Two-args-int-eax-int-literal:
Int-var-in-eax/imm32
Single-lit-var/imm32/next
Int-var-and-literal:
Int-var-in-mem/imm32
Single-lit-var/imm32/next
Int-var-in-register-and-literal:
Int-var-in-some-register/imm32
Single-lit-var/imm32/next
Single-int-var-in-some-register:
Int-var-in-some-register/imm32
0/imm32/next
Int-var-in-some-register:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
Any-register/imm32
Single-int-var-in-eax:
Int-var-in-eax/imm32
0/imm32/next
Int-var-in-eax:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"eax"/imm32/register
Single-int-var-in-ecx:
Int-var-in-ecx/imm32
0/imm32/next
Int-var-in-ecx:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"ecx"/imm32/register
Single-int-var-in-edx:
Int-var-in-edx/imm32
0/imm32/next
Int-var-in-edx:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"edx"/imm32/register
Single-int-var-in-ebx:
Int-var-in-ebx/imm32
0/imm32/next
Int-var-in-ebx:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"ebx"/imm32/register
Single-int-var-in-esi:
Int-var-in-esi/imm32
0/imm32/next
Int-var-in-esi:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"esi"/imm32/register
Single-int-var-in-edi:
Int-var-in-edi/imm32
0/imm32/next
Int-var-in-edi:
"arg1"/imm32/name
Type-int/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
"edi"/imm32/register
Single-lit-var:
Lit-var/imm32
0/imm32/next
Lit-var:
"literal"/imm32/name
Type-literal/imm32
1/imm32/some-block-depth
0/imm32/no-stack-offset
0/imm32/no-register
Type-int:
1/imm32/left/int
0/imm32/right/null
Type-literal:
0/imm32/left/literal
0/imm32/right/null
== code
emit-subx-primitive: # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# ecx = primitive
8b/-> *(ebp+0x10) 1/r32/ecx
# emit primitive name
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) *(ecx+0xc)) # Primitive-subx-name
# emit rm32 if necessary
(emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc)) # out, Primitive-subx-rm32, stmt
# emit r32 if necessary
(emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc)) # out, Primitive-subx-r32, stmt
# emit imm32 if necessary
(emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc)) # out, Primitive-subx-imm32, stmt
# emit disp32 if necessary
(emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc)) # out, Primitive-subx-disp32, stmt
(write-buffered *(ebp+8) Newline)
$emit-subx-primitive:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-rm32: # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
# if (l == 0) return
81 7/subop/compare *(ebp+0xc) 0/imm32
74/jump-if-= $emit-subx-rm32:end/disp8
# var v/eax: (handle var)
(get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax
(emit-subx-var-as-rm32 *(ebp+8) %eax)
$emit-subx-rm32:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
get-stmt-operand-from-arg-location: # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# eax = l
8b/-> *(ebp+0xc) 0/r32/eax
# ecx = stmt
8b/-> *(ebp+8) 1/r32/ecx
# if (l == 1) return stmt->inouts
{
3d/compare-eax-and 1/imm32
75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:1:
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
eb/jump $get-stmt-operand-from-arg-location:end/disp8
}
# if (l == 2) return stmt->inouts->next
{
3d/compare-eax-and 2/imm32
75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:2:
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
8b/-> *(eax+4) 0/r32/eax # Stmt-var-next
eb/jump $get-stmt-operand-from-arg-location:end/disp8
}
# if (l == 3) return stmt->outputs
{
3d/compare-eax-and 3/imm32
75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:3:
8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs
eb/jump $get-stmt-operand-from-arg-location:end/disp8
}
# abort
e9/jump $get-stmt-operand-from-arg-location:abort/disp32
$get-stmt-operand-from-arg-location:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$get-stmt-operand-from-arg-location:abort:
# error("invalid arg-location " eax)
(write-buffered Stderr "invalid arg-location ")
(print-int32-buffered Stderr %eax)
(write-buffered Stderr Newline)
(flush Stderr)
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
emit-subx-r32: # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# if (location == 0) return
81 7/subop/compare *(ebp+0xc) 0/imm32
0f 84/jump-if-= $emit-subx-r32:end/disp32
# var v/eax: (handle var)
(get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax
8b/-> *eax 0/r32/eax # Stmt-var-value
(maybe-get Registers *(eax+0x10) 8) # Var-register => eax: (addr register-index)
(write-buffered *(ebp+8) Space)
(print-int32-buffered *(ebp+8) *eax)
(write-buffered *(ebp+8) "/r32")
$emit-subx-r32:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-imm32: # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# if (location == 0) return
81 7/subop/compare *(ebp+0xc) 0/imm32
74/jump-if-= $emit-subx-imm32:end/disp8
# var v/eax: (handle var)
(get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax
8b/-> *eax 0/r32/eax # Stmt-var-value
(write-buffered *(ebp+8) Space)
(write-buffered *(ebp+8) *eax) # Var-name
(write-buffered *(ebp+8) "/imm32")
$emit-subx-imm32:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-disp32: # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
# if (location == 0) return
81 7/subop/compare *(ebp+0xc) 0/imm32
0f 84/jump-if-= $emit-subx-disp32:end/disp32
# var v/eax: (handle var)
(get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax
8b/-> *eax 0/r32/eax # Stmt-var-value
(write-buffered *(ebp+8) Space)
(write-buffered *(ebp+8) *eax) # Var-name
# hack: if instruction operation starts with "break", emit ":break"
# var name/ecx: (addr array byte) = stmt->operation
8b/-> *(ebp+0x10) 0/r32/eax
8b/-> *(eax+4) 1/r32/ecx
{
(string-starts-with? %ecx "break") # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(write-buffered *(ebp+8) ":break")
}
# hack: if instruction operation starts with "loop", emit ":loop"
{
(string-starts-with? %ecx "loop") # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(write-buffered *(ebp+8) ":loop")
}
(write-buffered *(ebp+8) "/disp32")
$emit-subx-disp32:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-call: # out: (addr buffered-file), stmt: (handle stmt), callee: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "(")
# - emit function name
8b/-> *(ebp+0x10) 1/r32/ecx
(write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name
# - emit arguments
# var curr/ecx: (handle stmt-var) = stmt->inouts
8b/-> *(ebp+0xc) 1/r32/ecx
8b/-> *(ecx+8) 1/r32/ecx # Stmt1-inouts
{
# if (curr == null) break
81 7/subop/compare %ecx 0/imm32
74/jump-if-= break/disp8
#
(emit-subx-call-operand *(ebp+8) %ecx)
# curr = curr->next
8b/-> *(ecx+4) 1/r32/ecx # Stmt-var-next
eb/jump loop/disp8
}
#
(write-buffered *(ebp+8) ")\n")
$emit-subx-call:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# like a function call, except we have no idea what function it is
# we hope it's defined in SubX and that the types are ok
emit-hailmary-call: # out: (addr buffered-file), stmt: (handle stmt)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "(")
# ecx = stmt
8b/-> *(ebp+0xc) 1/r32/ecx
# - emit function name
(write-buffered *(ebp+8) *(ecx+4)) # Stmt1-operation
# - emit arguments
# var curr/ecx: (handle stmt-var) = stmt->inouts
8b/-> *(ecx+8) 1/r32/ecx # Stmt1-inouts
{
# if (curr == null) break
81 7/subop/compare %ecx 0/imm32
74/jump-if-= break/disp8
#
(emit-subx-call-operand *(ebp+8) %ecx)
# curr = curr->next
8b/-> *(ecx+4) 1/r32/ecx # Stmt-var-next
eb/jump loop/disp8
}
#
(write-buffered *(ebp+8) ")\n")
$emit-hailmary-call:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-call-operand: # out: (addr buffered-file), s: (handle stmt-var)
# shares code with emit-subx-var-as-rm32
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
# ecx = s
8b/-> *(ebp+0xc) 1/r32/ecx
# var operand/esi: (handle var) = s->value
8b/-> *ecx 6/r32/esi # Stmt-var-value
# if (operand->register && s->is-deref?) emit "*__"
{
$emit-subx-call-operand:check-for-register-indirect:
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
74/jump-if-= break/disp8
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
74/jump-if-= break/disp8
$emit-subx-call-operand:register-indirect:
(write-buffered *(ebp+8) " *")
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
e9/jump $emit-subx-call-operand:end/disp32
}
# if (operand->register && !s->is-deref?) emit "%__"
{
$emit-subx-call-operand:check-for-register-direct:
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
74/jump-if-= break/disp8
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
75/jump-if-!= break/disp8
$emit-subx-call-operand:register-direct:
(write-buffered *(ebp+8) " %")
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
e9/jump $emit-subx-call-operand:end/disp32
}
# else if (operand->stack-offset) emit "*(ebp+__)"
{
81 7/subop/compare *(esi+0xc) 0/imm32 # Var-offset
74/jump-if-= break/disp8
$emit-subx-call-operand:stack:
(write-buffered *(ebp+8) Space)
(write-buffered *(ebp+8) "*(ebp+")
(print-int32-buffered *(ebp+8) *(esi+0xc)) # Var-offset
(write-buffered *(ebp+8) ")")
e9/jump $emit-subx-call-operand:end/disp32
}
# else if (operand->type == literal) emit "__"
{
8b/-> *(esi+4) 0/r32/eax # Var-type
81 7/subop/compare *eax 0/imm32 # Tree-left
75/jump-if-!= break/disp8
$emit-subx-call-operand:literal:
(write-buffered *(ebp+8) Space)
(write-buffered *(ebp+8) *esi)
}
$emit-subx-call-operand:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-var-as-rm32: # out: (addr buffered-file), s: (handle stmt-var)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
# ecx = s
8b/-> *(ebp+0xc) 1/r32/ecx
# var operand/esi: (handle var) = s->value
8b/-> *ecx 6/r32/esi # Stmt-var-value
# if (operand->register && s->is-deref?) emit "*__"
{
$emit-subx-var-as-rm32:check-for-register-indirect:
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
74/jump-if-= break/disp8
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
74/jump-if-= break/disp8
$emit-subx-var-as-rm32:register-indirect:
(write-buffered *(ebp+8) " *")
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
}
# if (operand->register && !s->is-deref?) emit "%__"
{
$emit-subx-var-as-rm32:check-for-register-direct:
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
74/jump-if-= break/disp8
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
75/jump-if-!= break/disp8
$emit-subx-var-as-rm32:register-direct:
(write-buffered *(ebp+8) " %")
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
}
# else if (operand->stack-offset) emit "*(ebp+__)"
{
81 7/subop/compare *(esi+0xc) 0/imm32 # Var-offset
74/jump-if-= break/disp8
$emit-subx-var-as-rm32:stack:
(write-buffered *(ebp+8) Space)
(write-buffered *(ebp+8) "*(ebp+")
(print-int32-buffered *(ebp+8) *(esi+0xc)) # Var-offset
(write-buffered *(ebp+8) ")")
}
$emit-subx-var-as-rm32:end:
# . restore registers
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-matching-function: # functions: (addr function), stmt: (handle stmt) -> result/eax: (handle function)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var curr/ecx: (handle function) = functions
8b/-> *(ebp+8) 1/r32/ecx
{
# if (curr == null) break
81 7/subop/compare %ecx 0/imm32
74/jump-if-= break/disp8
# if match(stmt, curr) return curr
{
(mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
89/<- %eax 1/r32/ecx
eb/jump $find-matching-function:end/disp8
}
# curr = curr->next
8b/-> *(ecx+0x14) 1/r32/ecx # Function-next
eb/jump loop/disp8
}
# return null
b8/copy-to-eax 0/imm32
$find-matching-function:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
find-matching-primitive: # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var curr/ecx: (handle primitive) = primitives
8b/-> *(ebp+8) 1/r32/ecx
{
$find-matching-primitive:loop:
# if (curr == null) break
81 7/subop/compare %ecx 0/imm32
0f 84/jump-if-= break/disp32
#? (write-buffered Stderr "prim: ")
#? (write-buffered Stderr *ecx) # Primitive-name
#? (write-buffered Stderr " => ")
#? (write-buffered Stderr *(ecx+0xc)) # Primitive-subx-name
#? (write-buffered Stderr Newline)
#? (flush Stderr)
# if match(curr, stmt) return curr
{
(mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
89/<- %eax 1/r32/ecx
eb/jump $find-matching-primitive:end/disp8
}
$find-matching-primitive:next-primitive:
# curr = curr->next
8b/-> *(ecx+0x24) 1/r32/ecx # Primitive-next
e9/jump loop/disp32
}
# return null
b8/copy-to-eax 0/imm32
$find-matching-primitive:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
mu-stmt-matches-function?: # stmt: (handle stmt), function: (handle function) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# return function->name == stmt->operation
8b/-> *(ebp+8) 1/r32/ecx
8b/-> *(ebp+0xc) 0/r32/eax
(string-equal? *(ecx+4) *eax) # Stmt1-operation, Function-name => eax
$mu-stmt-matches-function?:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
mu-stmt-matches-primitive?: # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
# A mu stmt matches a primitive if the name matches, all the inout vars
# match, and all the output vars match.
# Vars match if types match and registers match.
# In addition, a stmt output matches a primitive's output if types match
# and the primitive has a wildcard register.
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# ecx = stmt
8b/-> *(ebp+8) 1/r32/ecx
# edx = primitive
8b/-> *(ebp+0xc) 2/r32/edx
{
$mu-stmt-matches-primitive?:check-name:
# if (primitive->name != stmt->operation) return false
(string-equal? *(ecx+4) *edx) # Stmt1-operation, Primitive-name => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
b8/copy-to-eax 0/imm32
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
$mu-stmt-matches-primitive?:check-inouts:
# for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
8b/-> *(ecx+8) 6/r32/esi # Stmt1-inouts or Regvardef-inouts
8b/-> *(edx+4) 7/r32/edi # Primitive-inouts
{
# if (curr == 0 && curr2 == 0) move on to check outputs
{
81 7/subop/compare %esi 0/imm32
75/jump-if-!= break/disp8
$mu-stmt-matches-primitive?:stmt-inout-is-null:
{
81 7/subop/compare %edi 0/imm32
75/jump-if-!= break/disp8
#
e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
}
# return false
b8/copy-to-eax 0/imm32/false
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# if (curr2 == 0) return false
{
81 7/subop/compare %edi 0/imm32
75/jump-if-!= break/disp8
$mu-stmt-matches-primitive?:prim-inout-is-null:
b8/copy-to-eax 0/imm32/false
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# if (curr != curr2) return false
{
(operand-matches-primitive? %esi *edi) # List-value => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
b8/copy-to-eax 0/imm32/false
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# curr=curr->next
8b/-> *(esi+4) 6/r32/esi # Stmt-var-next
# curr2=curr2->next
8b/-> *(edi+4) 7/r32/edi # Stmt-var-next
eb/jump loop/disp8
}
$mu-stmt-matches-primitive?:check-outputs:
# for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
8b/-> *(ecx+0xc) 6/r32/esi # Stmt1-outputs
8b/-> *(edx+8) 7/r32/edi # Primitive-outputs
{
# if (curr == 0) return (curr2 == 0)
{
$mu-stmt-matches-primitive?:check-output:
81 7/subop/compare %esi 0/imm32
75/jump-if-!= break/disp8
{
81 7/subop/compare %edi 0/imm32
75/jump-if-!= break/disp8
# return true
b8/copy-to-eax 1/imm32
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# return false
b8/copy-to-eax 0/imm32
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# if (curr2 == 0) return false
{
81 7/subop/compare %edi 0/imm32
75/jump-if-!= break/disp8
b8/copy-to-eax 0/imm32
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# if (curr != curr2) return false
{
(operand-matches-primitive? %esi *edi) # List-value => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
b8/copy-to-eax 0/imm32
e9/jump $mu-stmt-matches-primitive?:end/disp32
}
# curr=curr->next
8b/-> *(esi+4) 6/r32/esi # Stmt-var-next
# curr2=curr2->next
8b/-> *(edi+4) 7/r32/edi # Stmt-var-next
eb/jump loop/disp8
}
$mu-stmt-matches-primitive?:return-true:
b8/copy-to-eax 1/imm32
$mu-stmt-matches-primitive?:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
operand-matches-primitive?: # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
56/push-esi
57/push-edi
# ecx = s
8b/-> *(ebp+8) 1/r32/ecx
# var var/esi : (handle var) = s->value
8b/-> *ecx 6/r32/esi # Stmt-var-value
# edi = prim-var
8b/-> *(ebp+0xc) 7/r32/edi
$operand-matches-primitive?:check-type:
# if (var->type != prim-var->type) return false
(subx-type-equal? *(esi+4) *(edi+4)) # Var-type, Var-type => eax
3d/compare-eax-and 0/imm32/false
b8/copy-to-eax 0/imm32/false
0f 84/jump-if-= $operand-matches-primitive?:end/disp32
{
$operand-matches-primitive?:check-register:
# if prim-var is in memory and var is in register but dereference, match
{
81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register
0f 85/jump-if-!= break/disp32
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
74/jump-if-= break/disp8
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
74/jump-if-= break/disp8
e9/jump $operand-matches-primitive?:return-true/disp32
}
# if prim-var is in register and var is in register but dereference, no match
{
81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register
0f 84/jump-if-= break/disp32
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
0f 84/jump-if-= break/disp32
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
74/jump-if-= break/disp8
e9/jump $operand-matches-primitive?:return-false/disp32
}
# return false if var->register doesn't match prim-var->register
{
# if register addresses are equal, it's a match
8b/-> *(esi+0x10) 0/r32/eax # Var-register
39/compare *(edi+0x10) 0/r32/eax # Var-register
74/jump-if-= break/disp8
# if either address is 0, return false
3d/compare-eax-and 0/imm32
74/jump-if-= $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result
81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register
74/jump-if-= $operand-matches-primitive?:return-false/disp8
# if prim-var->register is wildcard, it's a match
(string-equal? *(edi+0x10) Any-register) # Var-register => eax
3d/compare-eax-and 0/imm32/false
75/jump-if-!= break/disp8
# if string contents aren't equal, return false
(string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= $operand-matches-primitive?:return-false/disp8
}
}
$operand-matches-primitive?:return-true:
b8/copy-to-eax 1/imm32/true
eb/jump $operand-matches-primitive?:end/disp8
$operand-matches-primitive?:return-false:
b8/copy-to-eax 0/imm32/false
$operand-matches-primitive?:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
subx-type-equal?: # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var alit/ecx: boolean = is-literal-type?(a)
(is-literal-type? *(ebp+8)) # => eax
89/<- %ecx 0/r32/eax
# var blit/eax: boolean = is-literal-type?(b)
(is-literal-type? *(ebp+0xc)) # => eax
# return alit == blit
39/compare %eax 1/r32/ecx
74/jump-if-= $subx-type-equal?:true/disp8
$subx-type-equal?:false:
b8/copy-to-eax 0/imm32/false
eb/jump $subx-type-equal?:end/disp8
$subx-type-equal?:true:
b8/copy-to-eax 1/imm32/true
$subx-type-equal?:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
is-literal-type?: # a: (handle tree type-id) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
8b/-> *(ebp+8) 0/r32/eax
8b/-> *eax 0/r32/eax # Atom-value
3d/compare-eax-and 0/imm32/false
74/jump-if-equal $is-literal-type?:end/disp8
b8/copy-to-eax 1/imm32/true
$is-literal-type?:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-primitive:
# Primitive operation on a variable on the stack.
# increment foo
# =>
# ff 0/subop/increment *(ebp-8)
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# stack-offset: -8
#
# There's a primitive with this info:
# name: 'increment'
# inouts: int/mem
# value: 'ff 0/subop/increment'
#
# There's nothing in functions.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var
68/push 0/imm32/no-register
68/push -8/imm32/stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var operand/ebx: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %ebx 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
53/push-ebx/operands
68/push "increment"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var primitives/ebx: primitive
68/push 0/imm32/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 1/imm32/rm32-is-first-inout
68/push "ff 0/subop/increment"/imm32/subx-name
68/push 0/imm32/outputs
53/push-ebx/inouts # hack; in practice we won't have the same var in function definition and call
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-primitive-register:
# Primitive operation on a variable in a register.
# foo <- increment
# =>
# ff 0/subop/increment %eax # sub-optimal, but should suffice
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# register: 'eax'
#
# There's a primitive with this info:
# name: 'increment'
# out: int/reg
# value: 'ff 0/subop/increment'
#
# There's nothing in functions.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var operand/ebx: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %ebx 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
53/push-ebx/outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var formal-var/ebx: var in any register
68/push Any-register/imm32
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "dummy"/imm32
89/<- %ebx 4/r32/esp
# var operand/ebx: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
53/push-ebx/formal-var
89/<- %ebx 4/r32/esp
# var primitives/ebx: primitive
68/push 0/imm32/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 3/imm32/rm32-in-first-output
68/push "ff 0/subop/increment"/imm32/subx-name
53/push-ebx/outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-select-primitive:
# Select the right primitive between overloads.
# foo <- increment
# =>
# ff 0/subop/increment %eax # sub-optimal, but should suffice
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# register: 'eax'
#
# There's two primitives, as follows:
# - name: 'increment'
# out: int/reg
# value: 'ff 0/subop/increment'
# - name: 'increment'
# inout: int/mem
# value: 'ff 0/subop/increment'
#
# There's nothing in functions.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var real-outputs/edi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var formal-var/ebx: var in any register
68/push Any-register/imm32
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "dummy"/imm32
89/<- %ebx 4/r32/esp
# var formal-outputs/ebx: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
53/push-ebx/formal-var
89/<- %ebx 4/r32/esp
# var primitive1/ebx: primitive
68/push 0/imm32/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 3/imm32/rm32-in-first-output
68/push "ff 0/subop/increment"/imm32/subx-name
53/push-ebx/outputs/formal-outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# var primitives/ebx: primitive
53/push-ebx/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 1/imm32/rm32-is-first-inout
68/push "ff 0/subop/increment"/imm32/subx-name
68/push 0/imm32/outputs
57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-select-primitive-2:
# Select the right primitive between overloads.
# foo <- increment
# =>
# ff 0/subop/increment %eax # sub-optimal, but should suffice
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# register: 'eax'
#
# There's two primitives, as follows:
# - name: 'increment'
# out: int/reg
# value: 'ff 0/subop/increment'
# - name: 'increment'
# inout: int/mem
# value: 'ff 0/subop/increment'
#
# There's nothing in functions.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var inouts/edi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
57/push-edi/inouts
68/push "increment"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var formal-var/ebx: var in any register
68/push Any-register/imm32
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "dummy"/imm32
89/<- %ebx 4/r32/esp
# var operand/ebx: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
53/push-ebx/formal-var
89/<- %ebx 4/r32/esp
# var primitive1/ebx: primitive
68/push 0/imm32/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 3/imm32/rm32-in-first-output
68/push "ff 0/subop/increment"/imm32/subx-name
53/push-ebx/outputs/formal-outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# var primitives/ebx: primitive
53/push-ebx/next
68/push 0/imm32/output-is-write-only
68/push 0/imm32/no-disp32
68/push 0/imm32/no-imm32
68/push 0/imm32/no-r32
68/push 1/imm32/rm32-is-first-inout
68/push "ff 0/subop/increment"/imm32/subx-name
68/push 0/imm32/outputs
57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call
68/push "increment"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-increment-register:
# Select the right register between overloads.
# foo <- increment
# =>
# 50/increment-eax
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# register: 'eax'
#
# Primitives are the global definitions.
#
# There are no functions defined.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var real-outputs/edi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/operation
68/push 1/imm32/regular-stmt
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-increment-var:
# Select the right primitive between overloads.
# foo <- increment
# =>
# ff 0/subop/increment %eax # sub-optimal, but should suffice
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# register: 'eax'
#
# Primitives are the global definitions.
#
# There are no functions defined.
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var inouts/edi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
68/push 0/imm32/inouts
68/push "increment"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-reg-to-reg:
# var1/reg <- add var2/reg
# =>
# 01/add %var1 var2
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var var-var2/edx: var in ecx
68/push "ecx"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "var2"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var outputs/edi: (handle stmt-var) = [var1, var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-var1
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
56/push-esi/inouts
68/push "add"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-reg-to-mem:
# add-to var1 var2/reg
# =>
# 01/add *(ebp+__) var2
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var var-var2/edx: var in ecx
68/push "ecx"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "var2"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var inouts = (handle stmt-var) = [var1, var2]
56/push-esi/next
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "add-to"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-mem-to-reg:
# var1/reg <- add var2
# =>
# 03/add *(ebp+__) var1
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var var-var2/edx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "var2"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var outputs/edi = (handle stmt-var) = [var1]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-var1
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
56/push-esi/inouts
68/push "add"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-literal-to-eax:
# var1/eax <- add 0x34
# =>
# 05/add-to-eax 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var outputs/edi: (handle stmt-var) = [var1]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-var1
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
56/push-esi/inouts
68/push "add"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-literal-to-reg:
# var1/ecx <- add 0x34
# =>
# 81 0/subop/add %ecx 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in ecx
68/push "ecx"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var outputs/edi: (handle stmt-var) = [var1]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-var1
89/<- %edi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
57/push-edi/outputs
56/push-esi/inouts
68/push "add"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-add-literal-to-mem:
# add-to var1, 0x34
# =>
# 81 0/subop/add %eax 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# var inouts = (handle stmt-var) = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi/next
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "add-to"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-compare-mem-with-reg:
# compare var1, var2/eax
# =>
# 39/compare *(ebp+___) 0/r32/eax
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var2/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var2"/imm32
89/<- %ecx 4/r32/esp
# var var-var1/edx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "var1"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-var2
89/<- %esi 4/r32/esp
# inouts = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi
52/push-edx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "compare"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-compare-reg-with-mem:
# compare var1/eax, var2
# =>
# 3b/compare *(ebp+___) 0/r32/eax
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var var-var2/edx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
ff 6/subop/push *(ecx+4) # Var-type
68/push "var2"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# inouts = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "compare"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-compare-mem-with-literal:
# compare var1, 0x34
# =>
# 81 7/subop/compare *(ebp+___) 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var
68/push 0/imm32/no-register
68/push 8/imm32/stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# inouts = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi/next
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "compare"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-compare-eax-with-literal:
# compare var1/eax 0x34
# =>
# 3d/compare-eax-with 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in eax
68/push "eax"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# inouts = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi/next
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "compare"/imm32/operation
68/push 1/imm32/regular-stmt
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-compare-reg-with-literal:
# compare var1/ecx 0x34
# =>
# 81 7/subop/compare %ecx 0x34/imm32
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-var1/ecx: var in ecx
68/push "ecx"/imm32/register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
51/push-ecx
68/push "var1"/imm32
89/<- %ecx 4/r32/esp
# var type/edx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %edx 4/r32/esp
# var var-var2/edx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 1/imm32/block-depth
52/push-edx
68/push "0x34"/imm32
89/<- %edx 4/r32/esp
# var inouts/esi: (handle stmt-var) = [var2]
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
52/push-edx/var-var2
89/<- %esi 4/r32/esp
# inouts = [var1, var2]
68/push 0/imm32/is-deref:false
56/push-esi/next
51/push-ecx/var-var1
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "compare"/imm32/operation
68/push 1/imm32/regular-stmt
89/<- %esi 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-function-call:
# Call a function on a variable on the stack.
# f foo
# =>
# (f2 *(ebp-8))
# (Changing the function name supports overloading in general, but here it
# just serves to help disambiguate things.)
#
# There's a variable on the var stack as follows:
# name: 'foo'
# type: int
# stack-offset: -8
#
# There's nothing in primitives.
#
# There's a function with this info:
# name: 'f'
# inout: int/mem
# value: 'f2'
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = int
68/push 0/imm32/right/null
68/push 1/imm32/left/int
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var
68/push 0/imm32/no-register
68/push -8/imm32/stack-offset
68/push 0/imm32/block-depth
51/push-ecx
68/push "foo"/imm32
89/<- %ecx 4/r32/esp
# var inouts/esi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "f"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var functions/ebx: function
68/push 0/imm32/next
68/push 0/imm32/body
68/push 0/imm32/outputs
51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call
68/push "f2"/imm32/subx-name
68/push "f"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-function-call-with-literal-arg:
# Call a function on a literal.
# f 34
# =>
# (f2 34)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
(clear-stream _test-output-stream)
(clear-stream $_test-output-buffered-file->buffer)
# var type/ecx: (handle tree type-id) = literal
68/push 0/imm32/right/null
68/push 0/imm32/left/literal
89/<- %ecx 4/r32/esp
# var var-foo/ecx: var literal
68/push 0/imm32/no-register
68/push 0/imm32/no-stack-offset
68/push 0/imm32/block-depth
51/push-ecx
68/push "34"/imm32
89/<- %ecx 4/r32/esp
# var inouts/esi: (handle stmt-var)
68/push 0/imm32/is-deref:false
68/push 0/imm32/next
51/push-ecx/var-foo
89/<- %esi 4/r32/esp
# var stmt/esi: statement
68/push 0/imm32/next
68/push 0/imm32/outputs
56/push-esi/inouts
68/push "f"/imm32/operation
68/push 1/imm32
89/<- %esi 4/r32/esp
# var functions/ebx: function
68/push 0/imm32/next
68/push 0/imm32/body
68/push 0/imm32/outputs
51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call
68/push "f2"/imm32/subx-name
68/push "f"/imm32/name
89/<- %ebx 4/r32/esp
# convert
c7 0/subop/copy *Curr-block-depth 0/imm32
(emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
(flush _test-output-buffered-file)
#? # dump _test-output-stream {{{
#? (write 2 "^")
#? (write-stream 2 _test-output-stream)
#? (write 2 "$\n")
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-indent: # out: (addr buffered-file), n: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
# var i/eax: int = n
8b/-> *(ebp+0xc) 0/r32/eax
{
# if (i <= 0) break
3d/compare-eax-with 0/imm32
7e/jump-if-<= break/disp8
(write-buffered *(ebp+8) " ")
48/decrement-eax
eb/jump loop/disp8
}
$emit-indent:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-prologue: # out: (addr buffered-file)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(write-buffered *(ebp+8) " # . prologue\n")
(write-buffered *(ebp+8) " 55/push-ebp\n")
(write-buffered *(ebp+8) " 89/<- %ebp 4/r32/esp\n")
$emit-subx-prologue:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-epilogue: # out: (addr buffered-file)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(write-buffered *(ebp+8) " # . epilogue\n")
(write-buffered *(ebp+8) " 89/<- %esp 5/r32/ebp\n")
(write-buffered *(ebp+8) " 5d/pop-to-ebp\n")
(write-buffered *(ebp+8) " c3/return\n")
$emit-subx-epilogue:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return