Skip to content

Commit 3526d53

Browse files
committed
Eliminate constant branches during analysis
1 parent ddd1bea commit 3526d53

5 files changed

Lines changed: 78 additions & 0 deletions

File tree

benchmark/RESULTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ benchmarks; allocations are reported by Go's benchmark harness.
88
| AOT float kernel | 232 ms, ~17.0M allocs | 1.265 ms, 1 alloc | ~183× |
99
| AOT reduce pipeline | 95 ms, ~5.87M allocs | 1.905 ms, 48 allocs | ~50× |
1010
| Interpreter constant arithmetic | 113.27 ms | 101.99 ms | ~10% |
11+
| Interpreter constant branch | 304.61 ms, ~20.0M allocs | 59.45 ms, 6 allocs | ~5.1× |
1112

1213
The AOT baselines were captured before primitive float specialization and
1314
integer pipeline fusion. The interpreter comparison uses

benchmark/interpreter/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ go test ./benchmark/interpreter -bench . -benchmem -count 5 -benchtime 2s
1111
subexpression through a function call. It is intended to show whether shared
1212
compiler optimizations improve interpreted execution rather than parsing or
1313
startup.
14+
15+
`constant-branch` measures whether a literal predicate inside a numeric loop
16+
prevents the interpreter's existing typed-loop compiler from recognizing the
17+
surrounding loop.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package interpreterbench
2+
3+
import (
4+
"testing"
5+
6+
"github.com/glojurelang/glojure/pkg/lang"
7+
"github.com/glojurelang/glojure/pkg/runtime"
8+
)
9+
10+
func BenchmarkConstantBranch(b *testing.B) {
11+
runtime.ReadEval(`
12+
(defn benchmark-interpreter-constant-branch-run []
13+
(loop [i 0
14+
total 0]
15+
(if (< i 10000000)
16+
(recur (inc i)
17+
(if (< (+ 1 2) 4)
18+
(inc total)
19+
(+ total 2)))
20+
total)))`)
21+
ns := lang.GlobalEnv.CurrentNamespace()
22+
run := ns.FindInternedVar(
23+
lang.NewSymbol("benchmark-interpreter-constant-branch-run"),
24+
).Get().(lang.IFn)
25+
if got := run.Invoke(); got != int64(10000000) {
26+
b.Fatalf("run = %v, want 10000000", got)
27+
}
28+
29+
b.ReportAllocs()
30+
b.ResetTimer()
31+
for i := 0; i < b.N; i++ {
32+
benchmarkResult = run.Invoke()
33+
}
34+
}

pkg/compiler/analyze.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,12 @@ func (a *Analyzer) parseIf(form interface{}, env Env) (*ast.Node, error) {
690690
if err != nil {
691691
return nil, err
692692
}
693+
if testExpr.Op == ast.OpConst {
694+
if IsTruthy(testExpr.Sub.(*ast.ConstNode).Value) {
695+
return withRawForm(thenExpr, form), nil
696+
}
697+
return withRawForm(elseExpr, form), nil
698+
}
693699
n := ast.MakeNode(ast.OpIf, form)
694700
n.Env = env
695701
n.Sub = &ast.IfNode{

pkg/compiler/analyze_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,39 @@ func TestFoldLiteralNumberCallLeavesTrapAtRuntime(t *testing.T) {
8686
}
8787
}
8888

89+
func TestFoldLiteralIf(t *testing.T) {
90+
analyzer := &Analyzer{}
91+
tests := []struct {
92+
name string
93+
test interface{}
94+
want int64
95+
}{
96+
{name: "truthy", test: int64(0), want: 1},
97+
{name: "false", test: false, want: 2},
98+
{name: "nil", test: nil, want: 2},
99+
}
100+
for _, test := range tests {
101+
t.Run(test.name, func(t *testing.T) {
102+
form := lang.NewList(
103+
lang.NewSymbol("if"),
104+
test.test,
105+
int64(1),
106+
int64(2),
107+
)
108+
node, err := analyzer.parseIf(form, nil)
109+
if err != nil {
110+
t.Fatal(err)
111+
}
112+
if node.Op != ast.OpConst {
113+
t.Fatalf("folded if op = %v, want OpConst", node.Op)
114+
}
115+
if got := node.Sub.(*ast.ConstNode).Value; got != test.want {
116+
t.Fatalf("folded if = %v, want %v", got, test.want)
117+
}
118+
})
119+
}
120+
}
121+
89122
func TestContainsResidualUnquote(t *testing.T) {
90123
form := lang.NewList(
91124
lang.NewSymbol("."),

0 commit comments

Comments
 (0)