From 8aa1e8e21108c61fef32d527e07cf26b96b1d757 Mon Sep 17 00:00:00 2001 From: Emil Valeev Date: Sat, 7 Dec 2024 20:45:40 +0500 Subject: [PATCH] refactor(compiler): move graphReduction to IR package, move its call from irgen to golang backend --- internal/compiler/backend/golang/backend.go | 14 ++++--- .../compiler/backend/golang/funcmap_test.go | 2 +- .../compiler/{irgen => ir}/graph_reduction.go | 22 +++++------ .../{irgen => ir}/graph_reduction_test.go | 37 +++++++++---------- internal/compiler/irgen/irgen.go | 6 +-- internal/compiler/sourcecode/scope.go | 2 +- 6 files changed, 38 insertions(+), 45 deletions(-) rename internal/compiler/{irgen => ir}/graph_reduction.go (70%) rename internal/compiler/{irgen => ir}/graph_reduction_test.go (74%) diff --git a/internal/compiler/backend/golang/backend.go b/internal/compiler/backend/golang/backend.go index c30140c5..e783476e 100644 --- a/internal/compiler/backend/golang/backend.go +++ b/internal/compiler/backend/golang/backend.go @@ -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 } @@ -46,7 +48,7 @@ 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, @@ -54,7 +56,7 @@ func (b Backend) Emit(dst string, prog *ir.Program, trace bool) error { } 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) } @@ -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) { @@ -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) diff --git a/internal/compiler/backend/golang/funcmap_test.go b/internal/compiler/backend/golang/funcmap_test.go index 14cd3abf..330795a8 100644 --- a/internal/compiler/backend/golang/funcmap_test.go +++ b/internal/compiler/backend/golang/funcmap_test.go @@ -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) }) } diff --git a/internal/compiler/irgen/graph_reduction.go b/internal/compiler/ir/graph_reduction.go similarity index 70% rename from internal/compiler/irgen/graph_reduction.go rename to internal/compiler/ir/graph_reduction.go index 8b446896..81032dc4 100644 --- a/internal/compiler/irgen/graph_reduction.go +++ b/internal/compiler/ir/graph_reduction.go @@ -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), ) @@ -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 { @@ -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{}{} diff --git a/internal/compiler/irgen/graph_reduction_test.go b/internal/compiler/ir/graph_reduction_test.go similarity index 74% rename from internal/compiler/irgen/graph_reduction_test.go rename to internal/compiler/ir/graph_reduction_test.go index 5c9a0d18..1f81e79c 100644 --- a/internal/compiler/irgen/graph_reduction_test.go +++ b/internal/compiler/ir/graph_reduction_test.go @@ -1,53 +1,52 @@ -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"}, }, @@ -55,40 +54,38 @@ func TestReduceFinalGraph(t *testing.T) { { 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) }) } diff --git a/internal/compiler/irgen/irgen.go b/internal/compiler/irgen/irgen.go index c63ca1f3..4fdb3f50 100644 --- a/internal/compiler/irgen/irgen.go +++ b/internal/compiler/irgen/irgen.go @@ -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 } diff --git a/internal/compiler/sourcecode/scope.go b/internal/compiler/sourcecode/scope.go index 9a73f503..ddfd35b1 100644 --- a/internal/compiler/sourcecode/scope.go +++ b/internal/compiler/sourcecode/scope.go @@ -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