Skip to content

Commit

Permalink
refactor(compiler): move graphReduction to IR package, move its call …
Browse files Browse the repository at this point in the history
…from irgen to golang backend
  • Loading branch information
emil14 committed Dec 7, 2024
1 parent b9773ee commit 8aa1e8e
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 45 deletions.
14 changes: 8 additions & 6 deletions internal/compiler/backend/golang/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ var (
)

func (b Backend) Emit(dst string, prog *ir.Program, trace bool) error {
addrToChanVar, chanVarNames := b.getPortChansMap(prog.Connections)
// graph must not contain intermediate connections to be supported by runtime
prog.Connections = ir.GraphReduction(prog.Connections)

funcCalls, err := b.getFuncCalls(prog.Funcs, addrToChanVar)
addrToChanVar, chanVarNames := b.buildPortChanMap(prog.Connections)
funcCalls, err := b.buildFuncCalls(prog.Funcs, addrToChanVar)
if err != nil {
return err
}
Expand All @@ -46,15 +48,15 @@ func (b Backend) Emit(dst string, prog *ir.Program, trace bool) error {
return err
}

data := templateData{
tplData := templateData{
CompilerVersion: pkg.Version,
ChanVarNames: chanVarNames,
FuncCalls: funcCalls,
Trace: trace,
}

var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
if err := tmpl.Execute(&buf, tplData); err != nil {
return errors.Join(ErrExecTmpl, err)
}

Expand All @@ -69,7 +71,7 @@ func (b Backend) Emit(dst string, prog *ir.Program, trace bool) error {
return compiler.SaveFilesToDir(dst, files)
}

func (b Backend) getFuncCalls(
func (b Backend) buildFuncCalls(
funcs []ir.FuncCall,
addrToChanVar map[ir.PortAddr]string,
) ([]templateFuncCall, error) {
Expand Down Expand Up @@ -278,7 +280,7 @@ func (b Backend) insertRuntimeFiles(files map[string][]byte) error {
return nil
}

func (b Backend) getPortChansMap(connections map[ir.PortAddr]ir.PortAddr) (map[ir.PortAddr]string, []string) {
func (b Backend) buildPortChanMap(connections map[ir.PortAddr]ir.PortAddr) (map[ir.PortAddr]string, []string) {
portsCount := len(connections) * 2
varNames := make([]string, 0, portsCount)
addrToChanVar := make(map[ir.PortAddr]string, portsCount)
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/backend/golang/funcmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestGetPortChansMap(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, _ := b.getPortChansMap(tt.connections)
result, _ := b.buildPortChanMap(tt.connections)
assert.Equal(t, tt.expectedMap, result)
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package irgen
package ir

import "github.com/nevalang/neva/internal/compiler/ir"

// reduceFinalGraph transforms program to a state where it doesn't have intermediate connections.
func (Generator) reduceFinalGraph(connections map[ir.PortAddr]ir.PortAddr) map[ir.PortAddr]ir.PortAddr {
intermediatePorts := map[ir.PortAddr]struct{}{}
// GraphReduction removes intermediate connections and returns reduced graph.
func GraphReduction(connections map[PortAddr]PortAddr) map[PortAddr]PortAddr {
intermediatePorts := map[PortAddr]struct{}{}

withoutIntermediateReceivers := make(
map[ir.PortAddr]ir.PortAddr,
map[PortAddr]PortAddr,
len(connections),
)

Expand All @@ -23,7 +21,7 @@ func (Generator) reduceFinalGraph(connections map[ir.PortAddr]ir.PortAddr) map[i

// second pass: remove connections with intermediate senders
result := make(
map[ir.PortAddr]ir.PortAddr,
map[PortAddr]PortAddr,
len(withoutIntermediateReceivers),
)
for sender, receiver := range withoutIntermediateReceivers {
Expand All @@ -38,10 +36,10 @@ func (Generator) reduceFinalGraph(connections map[ir.PortAddr]ir.PortAddr) map[i
// getFinalReceiver returns the final receiver for a given port address.
// It also returns true if the given port address was intermediate, false otherwise.
func getFinalReceiver(
receiver ir.PortAddr,
connections map[ir.PortAddr]ir.PortAddr,
) (final ir.PortAddr, intermediate bool) {
visited := make(map[ir.PortAddr]struct{})
receiver PortAddr,
connections map[PortAddr]PortAddr,
) (final PortAddr, intermediate bool) {
visited := make(map[PortAddr]struct{})
current := receiver
for {
visited[current] = struct{}{}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,94 +1,91 @@
package irgen
package ir

import (
"testing"

"github.com/nevalang/neva/internal/compiler/ir"
"github.com/stretchr/testify/assert"
)

func TestReduceFinalGraph(t *testing.T) {
func Test_GraphReduction(t *testing.T) {
tests := []struct {
name string
input map[ir.PortAddr]ir.PortAddr
expected map[ir.PortAddr]ir.PortAddr
input map[PortAddr]PortAddr
expected map[PortAddr]PortAddr
}{
{
name: "simple_chain_reduction",
// a:foo -> b:bar; b:bar -> c:baz
input: map[ir.PortAddr]ir.PortAddr{
input: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "b", Port: "bar"},
{Path: "b", Port: "bar"}: {Path: "c", Port: "baz"},
},
// a:foo -> c:baz
expected: map[ir.PortAddr]ir.PortAddr{
expected: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "c", Port: "baz"},
},
},
{
name: "multiple_intermediate_nodes",
// a:foo -> b:bar; b:bar -> c:baz; c:baz -> d:qux
input: map[ir.PortAddr]ir.PortAddr{
input: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "b", Port: "bar"},
{Path: "b", Port: "bar"}: {Path: "c", Port: "baz"},
{Path: "c", Port: "baz"}: {Path: "d", Port: "qux"},
},
// a:foo -> d:qux
expected: map[ir.PortAddr]ir.PortAddr{
expected: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "d", Port: "qux"},
},
},
{
name: "branching_connections",
// a:foo -> b:bar; b:bar -> c:baz; b:qux -> d:quux
input: map[ir.PortAddr]ir.PortAddr{
input: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "b", Port: "bar"},
{Path: "b", Port: "bar"}: {Path: "c", Port: "baz"},
{Path: "b", Port: "qux"}: {Path: "d", Port: "quux"},
},
// a:foo -> c:baz; b:qux -> d:quux
expected: map[ir.PortAddr]ir.PortAddr{
expected: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "c", Port: "baz"},
{Path: "b", Port: "qux"}: {Path: "d", Port: "quux"},
},
},
{
name: "cyclic_connections",
// a:foo -> b:bar; b:bar -> c:baz; c:baz -> a:qux
input: map[ir.PortAddr]ir.PortAddr{
input: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "b", Port: "bar"},
{Path: "b", Port: "bar"}: {Path: "c", Port: "baz"},
{Path: "c", Port: "baz"}: {Path: "a", Port: "qux"},
},
// a:foo -> a:qux
expected: map[ir.PortAddr]ir.PortAddr{
expected: map[PortAddr]PortAddr{
{Path: "a", Port: "foo"}: {Path: "a", Port: "qux"},
},
},
{
name: "array_ports",
// a:foo[0] -> b:bar; b:bar -> c:baz[1]
input: map[ir.PortAddr]ir.PortAddr{
input: map[PortAddr]PortAddr{
{Path: "a", Port: "foo", Idx: 0, IsArray: true}: {Path: "b", Port: "bar"},
{Path: "b", Port: "bar"}: {Path: "c", Port: "baz", Idx: 1, IsArray: true},
},
// a:foo[0] -> c:baz[1]
expected: map[ir.PortAddr]ir.PortAddr{
expected: map[PortAddr]PortAddr{
{Path: "a", Port: "foo", Idx: 0, IsArray: true}: {Path: "c", Port: "baz", Idx: 1, IsArray: true}, // Direct connection from a:foo[0] to c:baz[1], preserving array indices
},
},
{
name: "empty_input",
input: map[ir.PortAddr]ir.PortAddr{},
expected: map[ir.PortAddr]ir.PortAddr{},
input: map[PortAddr]PortAddr{},
expected: map[PortAddr]PortAddr{},
},
}

gen := Generator{}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := gen.reduceFinalGraph(tt.input)
result := GraphReduction(tt.input)
assert.Equal(t, tt.expected, result)
})
}
Expand Down
6 changes: 1 addition & 5 deletions internal/compiler/irgen/irgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,8 @@ func (g Generator) Generate(
return nil, fmt.Errorf("process node: %w", err)
}

// graph reduction is not an optimization:
// it's a necessity for runtime not to have intermediate connections
reducedNet := g.reduceFinalGraph(result.Connections)

return &ir.Program{
Connections: reducedNet,
Connections: result.Connections,
Funcs: result.Funcs,
}, nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/sourcecode/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func NewScope(build Build, location core.Location) Scope {
}
}

// Scope is an object that provides access to program entities
// Scope is entity reference resolver
type Scope struct {
loc core.Location
build Build
Expand Down

0 comments on commit 8aa1e8e

Please sign in to comment.