From 89f375f4fcb745d16f4ce161063edf40b8e1ee03 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Fri, 20 Jun 2025 21:05:49 +0200 Subject: [PATCH] Added ssa package --- src/core/Function.go | 2 + src/ssa/Block.go | 11 +++ src/ssa/Function.go | 5 ++ src/ssa/Index.go | 3 + src/ssa/Instruction.go | 11 +++ src/ssa/Type.go | 30 ++++++++ src/ssa/bench_test.go | 158 +++++++++++++++++++++++++++++++++++++++++ src/ssa/ssa_test.go | 14 ++++ 8 files changed, 234 insertions(+) create mode 100644 src/ssa/Block.go create mode 100644 src/ssa/Function.go create mode 100644 src/ssa/Index.go create mode 100644 src/ssa/Instruction.go create mode 100644 src/ssa/Type.go create mode 100644 src/ssa/bench_test.go create mode 100644 src/ssa/ssa_test.go diff --git a/src/core/Function.go b/src/core/Function.go index 74fa130..7b19c52 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -4,11 +4,13 @@ import ( "fmt" "git.urbach.dev/cli/q/src/fs" + "git.urbach.dev/cli/q/src/ssa" "git.urbach.dev/cli/q/src/token" ) // Function is the smallest unit of code. type Function struct { + ssa.Function Name string UniqueName string File *fs.File diff --git a/src/ssa/Block.go b/src/ssa/Block.go new file mode 100644 index 0000000..30d854b --- /dev/null +++ b/src/ssa/Block.go @@ -0,0 +1,11 @@ +package ssa + +type Block struct { + Entry []*Block + Instructions []Instruction + Exit []*Block +} + +func (b *Block) Append(instr Instruction) { + b.Instructions = append(b.Instructions, instr) +} \ No newline at end of file diff --git a/src/ssa/Function.go b/src/ssa/Function.go new file mode 100644 index 0000000..1831743 --- /dev/null +++ b/src/ssa/Function.go @@ -0,0 +1,5 @@ +package ssa + +type Function struct { + Blocks []*Block +} \ No newline at end of file diff --git a/src/ssa/Index.go b/src/ssa/Index.go new file mode 100644 index 0000000..9f27529 --- /dev/null +++ b/src/ssa/Index.go @@ -0,0 +1,3 @@ +package ssa + +type Index = uint16 \ No newline at end of file diff --git a/src/ssa/Instruction.go b/src/ssa/Instruction.go new file mode 100644 index 0000000..dcccd4a --- /dev/null +++ b/src/ssa/Instruction.go @@ -0,0 +1,11 @@ +package ssa + +// Instruction is a "fat struct" for performance reasons. +// It contains all the fields necessary to represent all instruction types. +type Instruction struct { + Type Type + Parameters []Index + Int int64 + Float float64 + String string +} \ No newline at end of file diff --git a/src/ssa/Type.go b/src/ssa/Type.go new file mode 100644 index 0000000..c1aba45 --- /dev/null +++ b/src/ssa/Type.go @@ -0,0 +1,30 @@ +package ssa + +type Type byte + +const ( + None Type = iota + + // Values + Int + Float + String + + // Binary + Add + Sub + Mul + Div + Mod + + // Bitwise + And + Or + Xor + Shl + Shr + + // Special + Call + Phi +) \ No newline at end of file diff --git a/src/ssa/bench_test.go b/src/ssa/bench_test.go new file mode 100644 index 0000000..03799f3 --- /dev/null +++ b/src/ssa/bench_test.go @@ -0,0 +1,158 @@ +package ssa_test + +import ( + "os" + "runtime/debug" + "testing" + + "git.urbach.dev/go/assert" +) + +const n = 100 + +type FatStruct struct { + Type byte + A int + B int + C int + D int + E int + F int + G int +} + +type Instruction interface{} + +type BinaryInstruction struct { + A int + B int +} + +type OtherInstruction struct { + C int + D int + E int + F int + G int +} + +func TestMain(m *testing.M) { + debug.SetGCPercent(-1) + os.Exit(m.Run()) +} + +func BenchmarkFatStructRaw(b *testing.B) { + for b.Loop() { + entries := make([]FatStruct, 0, n) + + for i := range n { + entries = append(entries, FatStruct{ + Type: byte(i % 2), + A: i, + B: i, + }) + } + + count := 0 + + for _, entry := range entries { + switch entry.Type { + case 0: + count++ + case 1: + } + } + + assert.Equal(b, count, n/2) + } +} + +func BenchmarkFatStructPtr(b *testing.B) { + for b.Loop() { + entries := make([]*FatStruct, 0, n) + + for i := range n { + entries = append(entries, &FatStruct{ + Type: byte(i % 2), + A: i, + B: i, + }) + } + + count := 0 + + for _, entry := range entries { + switch entry.Type { + case 0: + count++ + case 1: + } + } + + assert.Equal(b, count, n/2) + } +} + +func BenchmarkInterfaceRaw(b *testing.B) { + for b.Loop() { + entries := make([]Instruction, 0, n) + + for i := range n { + if i%2 == 0 { + entries = append(entries, BinaryInstruction{ + A: i, + B: i, + }) + } else { + entries = append(entries, OtherInstruction{ + C: i, + D: i, + }) + } + } + + count := 0 + + for _, entry := range entries { + switch entry.(type) { + case BinaryInstruction: + count++ + case OtherInstruction: + } + } + + assert.Equal(b, count, n/2) + } +} + +func BenchmarkInterfacePtr(b *testing.B) { + for b.Loop() { + entries := make([]Instruction, 0, n) + + for i := range n { + if i%2 == 0 { + entries = append(entries, &BinaryInstruction{ + A: i, + B: i, + }) + } else { + entries = append(entries, &OtherInstruction{ + C: i, + D: i, + }) + } + } + + count := 0 + + for _, entry := range entries { + switch entry.(type) { + case *BinaryInstruction: + count++ + case *OtherInstruction: + } + } + + assert.Equal(b, count, n/2) + } +} \ No newline at end of file diff --git a/src/ssa/ssa_test.go b/src/ssa/ssa_test.go new file mode 100644 index 0000000..8b74df0 --- /dev/null +++ b/src/ssa/ssa_test.go @@ -0,0 +1,14 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" +) + +func TestBlock(t *testing.T) { + block := &ssa.Block{} + block.Append(ssa.Instruction{Type: ssa.Int, Int: 1}) + block.Append(ssa.Instruction{Type: ssa.Int, Int: 2}) + block.Append(ssa.Instruction{Type: ssa.Add, Parameters: []ssa.Index{0, 1}}) +} \ No newline at end of file