<div class="gmail_quote">Guards seem to be given a rather stepmotherly treatment in the compiler... whether any decent code is generated appear to be entirely up to the mercy of the peephole optimizer. That's worse than I'd imagined.<br>
<br>For instance,<br> g3(X) when X=:=foo orelse<br> X=:=bar orelse<br> X=:=baz -> ok.<br>generates the rather asymmetric<br> {select_val,{x,0},{f,11},{list,[{atom,foo},{f,12},{atom,bar},{f,12}]}}.<br>
{label,11}.<br> {test,is_eq_exact,{f,9},[{x,0},{atom,baz}]}.<br> {label,12}.<br> {move,{atom,ok},{x,0}}.<br> return.<br><br>And worse, in some cases like<br> h(X,Y) when (X=:=a andalso Y>0) -> aa;<br>
h(X,Y) when (X=:=b andalso Y>0) -> bb;<br> h(X,Y) when (X=:=c andalso Y>0) -> cc.<br>the size of the generated code is quadratic in the size of the source code:<br> {select_val,{x,0},<br> {f,29},<br>
{list,[{atom,a},{f,31},{atom,b},{f,33},{atom,c},{f,35}]}}.<br> {label,31}.<br> {test,is_lt,{f,32},[{integer,0},{x,1}]}.<br> {move,{atom,aa},{x,0}}.<br> return.<br> {label,32}.<br> {select_val,{x,0},{f,29},{list,[{atom,b},{f,33},{atom,c},{f,35}]}}.<br>
{label,33}.<br> {test,is_lt,{f,34},[{integer,0},{x,1}]}.<br> {move,{atom,bb},{x,0}}.<br> return.<br> {label,34}.<br> {test,is_eq_exact,{f,29},[{x,0},{atom,c}]}.<br> {label,35}.<br> {test,is_lt,{f,29},[{integer,0},{x,1}]}.<br>
{move,{atom,cc},{x,0}}.<br> return.<br>(Notice the tables in the select_val instructions.)<br><br>This bad behaviour happens after the transition to beam code (in beam_dead:combine_eqs(), afaict).<br>Looks like guards really would like to be taken into account during pattern match compilation... (no offence, Dijkstra).<br>
<br>Anyway, the first roadblock appears to be that at the time when match compilation occurs (at the core->kernel representation transition), guards have already been flattened, meaning that what was at guard top-level in the source code is more difficult to find.<br>
For this reason, I'm considering a representation change of Core's clauses - from<br> -record(c_clause, {anno=[], pats, % pats :: [Tree],<br> guard, % guard :: Tree,<br> body}). % body :: Tree<br>
to<br> -record(c_clause, {anno=[], pats,<br>
guard_pats :: [{Tree, Tree}], % {ExprToTest, Pattern}<br> guard_exp :: Tree, <br>
body}).<br>such that the guard is split into a part that can integrated into the pattern matching step, and a remainder that can't.<br>
For instance, the guard "when Y > 0, element(1,X) =:= foo" would be represented as<br> guard_pats = [{E1, P1}], guard_exp=E2<br>where E1 = repr. of "element(1,X)", P1 = repr. of "'foo'", and E2 = repr. of "Y > 0".<br>
<br>Handling "P ?= E" at guard top level would then follow quite naturally: by including {E,P} in guard_pats.<br>(As for scoping, it is handled in an earlier phase.)<br>Integrating type tests need a bit more, but that can be left for later if necessary.<br>
<br>/Erik<br><br>Den 18. apr. 2012 14.54 skrev Erik Søe Sørensen <span dir="ltr"><<a href="mailto:eriksoe@gmail.com">eriksoe@gmail.com</a>></span>:<br>[snip]<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Handling 'orelse' might be tricky, though; the current approach for ';' appears to be to simply duplicate the clause and let a downstream optimizer recognize the duplication, so I suppose that will work for 'orelse' as well.<br>
(Looking at generated code, I notice that 'orelse' and 'andalso' in guards gives rise (in R14B03) to some surprising - and suboptimal - code.)<br></blockquote></div><br><br>