From 4e410452f6f1781fd257ba45f898f9b99e8f6837 Mon Sep 17 00:00:00 2001 From: ashiven Date: Wed, 17 Sep 2025 05:55:42 +0200 Subject: [PATCH 1/7] fix test cases to use the generated executable instead of lli --- .gitignore | 11 +++++----- Makefile | 8 ++++---- main.go | 20 ++++++++++++------- .../and-no-side-effects.choc | 2 +- .../and-side-effects.choc | 2 +- .../if-else-no-side-effects.choc | 2 +- .../if-else-side-effects.choc | 2 +- .../or-no-side-effects.choc | 2 +- .../or-side-effects.choc | 2 +- .../single-if-else.choc | 2 +- .../single-op-add.choc | 2 +- .../single-op-and.choc | 2 +- .../single-op-div.choc | 2 +- .../single-op-eq.choc | 2 +- .../single-op-ge.choc | 2 +- .../single-op-gt.choc | 2 +- .../single-op-is.choc | 2 +- .../single-op-le.choc | 2 +- .../single-op-lt.choc | 2 +- .../single-op-minus.choc | 2 +- .../single-op-mod.choc | 2 +- .../single-op-mul.choc | 2 +- .../single-op-ne.choc | 2 +- .../single-op-not.choc | 2 +- .../single-op-or.choc | 2 +- .../single-op-unary-minus.choc | 2 +- .../associativity-folding.choc | 2 +- tests/code-size-optimization/if-constant.choc | 2 +- .../pure-bool-function.choc | 2 +- .../pure-integer-function.choc | 2 +- .../variable-allocation-big.choc | 2 +- .../variable-allocation-loop.choc | 2 +- .../variable-allocation.choc | 2 +- tests/complete-programs/fib.choc | 2 +- tests/complete-programs/str-to-int.choc | 2 +- tests/control-flow/if-else-false.choc | 2 +- tests/control-flow/if-else-true.choc | 2 +- tests/control-flow/single-if-false.choc | 2 +- tests/control-flow/single-if-true.choc | 2 +- tests/control-flow/while-multiple-times.choc | 2 +- .../call-one-arg-with-return.choc | 2 +- tests/function-calls/call-one-arg.choc | 2 +- tests/function-returns/return-input.choc | 2 +- .../return-list-concat-var.choc | 2 +- .../function-returns/return-list-concat.choc | 2 +- tests/function-returns/return-list-index.choc | 2 +- .../function-returns/return-list-literal.choc | 2 +- tests/function-returns/return-list-var.choc | 2 +- .../return-string-concat-var.choc | 2 +- .../return-string-concat.choc | 2 +- .../function-returns/return-string-index.choc | 2 +- .../return-string-literal.choc | 2 +- tests/function-returns/return-string-var.choc | 2 +- tests/lists/combine-lists.choc | 2 +- tests/lists/for-list.choc | 2 +- tests/lists/for-none.choc | 2 +- tests/lists/list-index-nested.choc | 2 +- tests/lists/list-index-oob-negative.choc | 2 +- tests/lists/list-index-oob.choc | 2 +- tests/lists/list-index.choc | 2 +- tests/lists/list-len.choc | 2 +- tests/lists/list-none-len.choc | 2 +- tests/lists/list-of-string.choc | 2 +- tests/lists/none-index.choc | 2 +- tests/others/func-def-double.choc | 2 +- tests/others/if-elif-else.choc | 2 +- tests/others/ifexpr.choc | 2 +- tests/others/local-str-def.choc | 2 +- tests/others/single-stmt-while.choc | 2 +- tests/pass.choc | 2 +- tests/print-integer-literal.choc | 2 +- tests/strings/concat.choc | 2 +- tests/strings/equal.choc | 2 +- tests/strings/index.choc | 2 +- tests/strings/literals.choc | 2 +- tests/strings/read-str.choc | 2 +- tests/strings/single-str-def.choc | 2 +- tests/strings/string-for-loop.choc | 2 +- tests/var-defs/global-var.choc | 2 +- tests/var-defs/multi-assign-order.choc | 2 +- tests/var-defs/multi-assign.choc | 2 +- tests/var-defs/multiple-defs.choc | 2 +- tests/var-defs/rewrite-int-def.choc | 2 +- tests/var-defs/single-bool-def.choc | 2 +- tests/var-defs/single-int-def.choc | 2 +- tests/var-defs/var-def-in-func.choc | 2 +- 86 files changed, 106 insertions(+), 99 deletions(-) diff --git a/.gitignore b/.gitignore index 672ef8f..2b8effc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ cgp* -*.ll -*.o + +venv + +tests +!tests/*.choc + Output .lit_test_times.txt -tests-partial -*.ignore -venv diff --git a/Makefile b/Makefile index 70a37ca..e2145af 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ .PHONY: test, compile, clean -test: clean cgp compile +test: clean compile lit -v tests cgp: go build -tags=llvm18 -o cgp -compile: - find tests -type f -name "*.choc" -exec ./cgp -c {} \; >/dev/null 2>&1 +compile: cgp + find tests -type f -name "*.choc" -exec ./cgp {} \; >/dev/null 2>&1 clean: - find tests -type f -name "*.ll" -delete + find tests -type f ! \( -name "*.choc" -o -name "lit.cfg" \) -delete rm -f cgp diff --git a/main.go b/main.go index 70c94b4..9db8692 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,18 @@ package main import ( + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "chogopy/pkg/backend" "chogopy/pkg/codegen" "chogopy/pkg/lexer" "chogopy/pkg/parser" "chogopy/pkg/scopes" "chogopy/pkg/typechecks" - "log" - "os" - "os/exec" - "path/filepath" - "strings" "github.com/kr/pretty" "tinygo.org/x/go-llvm" @@ -76,7 +77,7 @@ func main() { err := os.WriteFile( filePath+".ll", []byte(codeGenerator.Module.String()), - 0644, + 0o644, ) if err != nil { panic(err) @@ -96,7 +97,7 @@ func main() { err := os.WriteFile( llFilePath, []byte(codeGenerator.Module.String()), - 0644, + 0o644, ) if err != nil { log.Fatalln("Failed to create llvm IR file: ", err) @@ -134,6 +135,11 @@ func main() { log.Fatalln("Failed to link object file: ", err) } + // TODO: The below should be specifiable with an argument. + // Move the output file into the same directory as the source code + outputPath := filepath.Join(filepath.Dir(filePath), outputFile) + os.Rename(outputFile, outputPath) + os.Remove(objectFilePath) } } diff --git a/tests/arithmetic-comparison-ops/and-no-side-effects.choc b/tests/arithmetic-comparison-ops/and-no-side-effects.choc index 4bb6c8d..30effef 100644 --- a/tests/arithmetic-comparison-ops/and-no-side-effects.choc +++ b/tests/arithmetic-comparison-ops/and-no-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./and-no-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 5 diff --git a/tests/arithmetic-comparison-ops/and-side-effects.choc b/tests/arithmetic-comparison-ops/and-side-effects.choc index a2e45e2..fa5e58d 100644 --- a/tests/arithmetic-comparison-ops/and-side-effects.choc +++ b/tests/arithmetic-comparison-ops/and-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./and-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 5 diff --git a/tests/arithmetic-comparison-ops/if-else-no-side-effects.choc b/tests/arithmetic-comparison-ops/if-else-no-side-effects.choc index f047b28..7295972 100644 --- a/tests/arithmetic-comparison-ops/if-else-no-side-effects.choc +++ b/tests/arithmetic-comparison-ops/if-else-no-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-else-no-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 5 diff --git a/tests/arithmetic-comparison-ops/if-else-side-effects.choc b/tests/arithmetic-comparison-ops/if-else-side-effects.choc index 774de64..e939fd8 100644 --- a/tests/arithmetic-comparison-ops/if-else-side-effects.choc +++ b/tests/arithmetic-comparison-ops/if-else-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-else-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 0 diff --git a/tests/arithmetic-comparison-ops/or-no-side-effects.choc b/tests/arithmetic-comparison-ops/or-no-side-effects.choc index 10c2dc6..a66b94f 100644 --- a/tests/arithmetic-comparison-ops/or-no-side-effects.choc +++ b/tests/arithmetic-comparison-ops/or-no-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./or-no-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 5 diff --git a/tests/arithmetic-comparison-ops/or-side-effects.choc b/tests/arithmetic-comparison-ops/or-side-effects.choc index ad1d950..3d6aed8 100644 --- a/tests/arithmetic-comparison-ops/or-side-effects.choc +++ b/tests/arithmetic-comparison-ops/or-side-effects.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./or-side-effects | filecheck %s # RUN: python %s | filecheck %s x: int = 5 diff --git a/tests/arithmetic-comparison-ops/single-if-else.choc b/tests/arithmetic-comparison-ops/single-if-else.choc index 7f5c7a0..3e7c4a1 100644 --- a/tests/arithmetic-comparison-ops/single-if-else.choc +++ b/tests/arithmetic-comparison-ops/single-if-else.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-if-else | filecheck %s # RUN: python %s | filecheck %s print(52 if True else 12) diff --git a/tests/arithmetic-comparison-ops/single-op-add.choc b/tests/arithmetic-comparison-ops/single-op-add.choc index 873cb7d..e54e97c 100644 --- a/tests/arithmetic-comparison-ops/single-op-add.choc +++ b/tests/arithmetic-comparison-ops/single-op-add.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-add | filecheck %s # RUN: python %s | filecheck %s print(0 + 0) diff --git a/tests/arithmetic-comparison-ops/single-op-and.choc b/tests/arithmetic-comparison-ops/single-op-and.choc index e1017e4..91615d2 100644 --- a/tests/arithmetic-comparison-ops/single-op-and.choc +++ b/tests/arithmetic-comparison-ops/single-op-and.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-and | filecheck %s # RUN: python %s | filecheck %s print(True and False) diff --git a/tests/arithmetic-comparison-ops/single-op-div.choc b/tests/arithmetic-comparison-ops/single-op-div.choc index d4a2ab4..3797b20 100644 --- a/tests/arithmetic-comparison-ops/single-op-div.choc +++ b/tests/arithmetic-comparison-ops/single-op-div.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-div | filecheck %s # RUN: python %s | filecheck %s print(1 // 1) diff --git a/tests/arithmetic-comparison-ops/single-op-eq.choc b/tests/arithmetic-comparison-ops/single-op-eq.choc index 22890d3..f3cc69c 100644 --- a/tests/arithmetic-comparison-ops/single-op-eq.choc +++ b/tests/arithmetic-comparison-ops/single-op-eq.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-eq | filecheck %s # RUN: python %s | filecheck %s print(1 == 1) diff --git a/tests/arithmetic-comparison-ops/single-op-ge.choc b/tests/arithmetic-comparison-ops/single-op-ge.choc index 1af66d5..97c750b 100644 --- a/tests/arithmetic-comparison-ops/single-op-ge.choc +++ b/tests/arithmetic-comparison-ops/single-op-ge.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-ge | filecheck %s # RUN: python %s | filecheck %s print(0 >= 1) diff --git a/tests/arithmetic-comparison-ops/single-op-gt.choc b/tests/arithmetic-comparison-ops/single-op-gt.choc index 3b76ce2..8ae544b 100644 --- a/tests/arithmetic-comparison-ops/single-op-gt.choc +++ b/tests/arithmetic-comparison-ops/single-op-gt.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-gt | filecheck %s # RUN: python %s | filecheck %s print(0 > 1) diff --git a/tests/arithmetic-comparison-ops/single-op-is.choc b/tests/arithmetic-comparison-ops/single-op-is.choc index fb49020..3ba9b28 100644 --- a/tests/arithmetic-comparison-ops/single-op-is.choc +++ b/tests/arithmetic-comparison-ops/single-op-is.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-is | filecheck %s # RUN: python %s | filecheck %s l: [int] = None diff --git a/tests/arithmetic-comparison-ops/single-op-le.choc b/tests/arithmetic-comparison-ops/single-op-le.choc index 827433d..1fb77d4 100644 --- a/tests/arithmetic-comparison-ops/single-op-le.choc +++ b/tests/arithmetic-comparison-ops/single-op-le.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-le | filecheck %s # RUN: python %s | filecheck %s print(1 <= 1) diff --git a/tests/arithmetic-comparison-ops/single-op-lt.choc b/tests/arithmetic-comparison-ops/single-op-lt.choc index 972ae3c..adf3b7e 100644 --- a/tests/arithmetic-comparison-ops/single-op-lt.choc +++ b/tests/arithmetic-comparison-ops/single-op-lt.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-lt | filecheck %s # RUN: python %s | filecheck %s print(1 < 1) diff --git a/tests/arithmetic-comparison-ops/single-op-minus.choc b/tests/arithmetic-comparison-ops/single-op-minus.choc index 58b3250..ae15618 100644 --- a/tests/arithmetic-comparison-ops/single-op-minus.choc +++ b/tests/arithmetic-comparison-ops/single-op-minus.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-minus | filecheck %s # RUN: python %s | filecheck %s print(1) diff --git a/tests/arithmetic-comparison-ops/single-op-mod.choc b/tests/arithmetic-comparison-ops/single-op-mod.choc index 3b899f2..f5c234c 100644 --- a/tests/arithmetic-comparison-ops/single-op-mod.choc +++ b/tests/arithmetic-comparison-ops/single-op-mod.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-mod | filecheck %s # RUN: python %s | filecheck %s print(0 % 1) diff --git a/tests/arithmetic-comparison-ops/single-op-mul.choc b/tests/arithmetic-comparison-ops/single-op-mul.choc index de02bee..7ce3db8 100644 --- a/tests/arithmetic-comparison-ops/single-op-mul.choc +++ b/tests/arithmetic-comparison-ops/single-op-mul.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-mul | filecheck %s # RUN: python %s | filecheck %s print(0 * 1) diff --git a/tests/arithmetic-comparison-ops/single-op-ne.choc b/tests/arithmetic-comparison-ops/single-op-ne.choc index 20686ac..da6564a 100644 --- a/tests/arithmetic-comparison-ops/single-op-ne.choc +++ b/tests/arithmetic-comparison-ops/single-op-ne.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-ne | filecheck %s # RUN: python %s | filecheck %s print(1 != 1) diff --git a/tests/arithmetic-comparison-ops/single-op-not.choc b/tests/arithmetic-comparison-ops/single-op-not.choc index 2d28045..838d5d9 100644 --- a/tests/arithmetic-comparison-ops/single-op-not.choc +++ b/tests/arithmetic-comparison-ops/single-op-not.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-not | filecheck %s # RUN: python %s | filecheck %s print(not True) diff --git a/tests/arithmetic-comparison-ops/single-op-or.choc b/tests/arithmetic-comparison-ops/single-op-or.choc index 1537b24..111126a 100644 --- a/tests/arithmetic-comparison-ops/single-op-or.choc +++ b/tests/arithmetic-comparison-ops/single-op-or.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-or | filecheck %s # RUN: python %s | filecheck %s print(True or False) diff --git a/tests/arithmetic-comparison-ops/single-op-unary-minus.choc b/tests/arithmetic-comparison-ops/single-op-unary-minus.choc index cd13368..5e210b4 100644 --- a/tests/arithmetic-comparison-ops/single-op-unary-minus.choc +++ b/tests/arithmetic-comparison-ops/single-op-unary-minus.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-op-unary-minus | filecheck %s # RUN: python %s | filecheck %s diff --git a/tests/code-size-optimization/associativity-folding.choc b/tests/code-size-optimization/associativity-folding.choc index b677462..7fe1e68 100644 --- a/tests/code-size-optimization/associativity-folding.choc +++ b/tests/code-size-optimization/associativity-folding.choc @@ -1,4 +1,4 @@ -# RUN: echo "43" | lli %s.ll | filecheck %s +# RUN: echo "43" | ./associativity-folding | filecheck %s # RUN: echo "43" | python %s | filecheck %s def char_to_int(c: str) -> int: diff --git a/tests/code-size-optimization/if-constant.choc b/tests/code-size-optimization/if-constant.choc index d999189..aa6a75a 100644 --- a/tests/code-size-optimization/if-constant.choc +++ b/tests/code-size-optimization/if-constant.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-constant | filecheck %s # RUN: python %s | filecheck %s if True: diff --git a/tests/code-size-optimization/pure-bool-function.choc b/tests/code-size-optimization/pure-bool-function.choc index 327b4a4..e56d89c 100644 --- a/tests/code-size-optimization/pure-bool-function.choc +++ b/tests/code-size-optimization/pure-bool-function.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./pure-bool-function | filecheck %s # RUN: python %s | filecheck %s b: bool = True diff --git a/tests/code-size-optimization/pure-integer-function.choc b/tests/code-size-optimization/pure-integer-function.choc index ea55756..2134769 100644 --- a/tests/code-size-optimization/pure-integer-function.choc +++ b/tests/code-size-optimization/pure-integer-function.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./pure-integer-function | filecheck %s # RUN: python %s | filecheck %s x: int = 10 diff --git a/tests/code-size-optimization/variable-allocation-big.choc b/tests/code-size-optimization/variable-allocation-big.choc index febb86a..9c6343c 100644 --- a/tests/code-size-optimization/variable-allocation-big.choc +++ b/tests/code-size-optimization/variable-allocation-big.choc @@ -1,4 +1,4 @@ -# RUN: echo "41" | lli %s.ll | filecheck %s +# RUN: echo "41" | ./variable-allocation-big | filecheck %s # RUN: echo "41" | python %s | filecheck %s def char_to_int(c: str) -> int: diff --git a/tests/code-size-optimization/variable-allocation-loop.choc b/tests/code-size-optimization/variable-allocation-loop.choc index 9fe59a5..3e690a6 100644 --- a/tests/code-size-optimization/variable-allocation-loop.choc +++ b/tests/code-size-optimization/variable-allocation-loop.choc @@ -1,4 +1,4 @@ -# RUN: echo "32" | lli %s.ll | filecheck %s +# RUN: echo "32" | ./variable-allocation-loop | filecheck %s # RUN: echo "32" | python %s | filecheck %s def char_to_int(c: str) -> int: diff --git a/tests/code-size-optimization/variable-allocation.choc b/tests/code-size-optimization/variable-allocation.choc index 15c615e..5093dc2 100644 --- a/tests/code-size-optimization/variable-allocation.choc +++ b/tests/code-size-optimization/variable-allocation.choc @@ -1,4 +1,4 @@ -# RUN: echo "41" | lli %s.ll | filecheck %s +# RUN: echo "41" | ./variable-allocation | filecheck %s # RUN: echo "41" | python %s | filecheck %s def char_to_int(c: str) -> int: diff --git a/tests/complete-programs/fib.choc b/tests/complete-programs/fib.choc index 2b17546..5dfa1fb 100644 --- a/tests/complete-programs/fib.choc +++ b/tests/complete-programs/fib.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./fib | filecheck %s # RUN: python %s | filecheck %s def fib(n: int) -> int: diff --git a/tests/complete-programs/str-to-int.choc b/tests/complete-programs/str-to-int.choc index 1a2f98e..6cba570 100644 --- a/tests/complete-programs/str-to-int.choc +++ b/tests/complete-programs/str-to-int.choc @@ -1,4 +1,4 @@ -# RUN: echo "2022" | lli %s.ll | filecheck %s +# RUN: echo "2022" | ./str-to-int | filecheck %s # RUN: echo "2022" | python %s | filecheck %s def char_to_int(c: str) -> int: diff --git a/tests/control-flow/if-else-false.choc b/tests/control-flow/if-else-false.choc index 15d918e..d850335 100644 --- a/tests/control-flow/if-else-false.choc +++ b/tests/control-flow/if-else-false.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-else-false | filecheck %s # RUN: python %s | filecheck %s if False: diff --git a/tests/control-flow/if-else-true.choc b/tests/control-flow/if-else-true.choc index 1488540..2f516d0 100644 --- a/tests/control-flow/if-else-true.choc +++ b/tests/control-flow/if-else-true.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-else-true | filecheck %s # RUN: python %s | filecheck %s if True: diff --git a/tests/control-flow/single-if-false.choc b/tests/control-flow/single-if-false.choc index dfb52d4..fcb87af 100644 --- a/tests/control-flow/single-if-false.choc +++ b/tests/control-flow/single-if-false.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-if-false | filecheck %s # RUN: python %s | filecheck %s if False: diff --git a/tests/control-flow/single-if-true.choc b/tests/control-flow/single-if-true.choc index 7774e48..0769230 100644 --- a/tests/control-flow/single-if-true.choc +++ b/tests/control-flow/single-if-true.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-if-true | filecheck %s # RUN: python %s | filecheck %s if True: diff --git a/tests/control-flow/while-multiple-times.choc b/tests/control-flow/while-multiple-times.choc index b0ea5f8..91f14f0 100644 --- a/tests/control-flow/while-multiple-times.choc +++ b/tests/control-flow/while-multiple-times.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./while-multiple-times | filecheck %s # RUN: python %s | filecheck %s x: int = 0 diff --git a/tests/function-calls/call-one-arg-with-return.choc b/tests/function-calls/call-one-arg-with-return.choc index ad42ac9..6f04e4c 100644 --- a/tests/function-calls/call-one-arg-with-return.choc +++ b/tests/function-calls/call-one-arg-with-return.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./call-one-arg-with-return | filecheck %s # RUN: python %s | filecheck %s def foo(x: int) -> int: diff --git a/tests/function-calls/call-one-arg.choc b/tests/function-calls/call-one-arg.choc index 0f9f99c..098f8a7 100644 --- a/tests/function-calls/call-one-arg.choc +++ b/tests/function-calls/call-one-arg.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./call-one-arg | filecheck %s # RUN: python %s | filecheck %s def foo(x: int): diff --git a/tests/function-returns/return-input.choc b/tests/function-returns/return-input.choc index 1b05818..a5a6498 100644 --- a/tests/function-returns/return-input.choc +++ b/tests/function-returns/return-input.choc @@ -1,4 +1,4 @@ -# RUN: echo "hello" | lli %s.ll | filecheck %s +# RUN: echo "hello" | ./return-input | filecheck %s # RUN: echo "hello" | python %s | filecheck %s a: str = "" diff --git a/tests/function-returns/return-list-concat-var.choc b/tests/function-returns/return-list-concat-var.choc index e249546..57531ea 100644 --- a/tests/function-returns/return-list-concat-var.choc +++ b/tests/function-returns/return-list-concat-var.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-list-concat-var | filecheck %s # RUN: python %s | filecheck %s # NOTE: Broken in executable diff --git a/tests/function-returns/return-list-concat.choc b/tests/function-returns/return-list-concat.choc index f2f6f53..516f370 100644 --- a/tests/function-returns/return-list-concat.choc +++ b/tests/function-returns/return-list-concat.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-list-concat | filecheck %s # RUN: python %s | filecheck %s # NOTE: Broken in executable diff --git a/tests/function-returns/return-list-index.choc b/tests/function-returns/return-list-index.choc index 7fe3b90..67039d3 100644 --- a/tests/function-returns/return-list-index.choc +++ b/tests/function-returns/return-list-index.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-list-index | filecheck %s # RUN: python %s | filecheck %s l: int = 0 diff --git a/tests/function-returns/return-list-literal.choc b/tests/function-returns/return-list-literal.choc index a1ac759..5fd9564 100644 --- a/tests/function-returns/return-list-literal.choc +++ b/tests/function-returns/return-list-literal.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-list-literal | filecheck %s # RUN: python %s | filecheck %s l: [int] = None diff --git a/tests/function-returns/return-list-var.choc b/tests/function-returns/return-list-var.choc index d6a95c0..f6418a9 100644 --- a/tests/function-returns/return-list-var.choc +++ b/tests/function-returns/return-list-var.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-list-var | filecheck %s # RUN: python %s | filecheck %s l: [int] = None diff --git a/tests/function-returns/return-string-concat-var.choc b/tests/function-returns/return-string-concat-var.choc index 5404068..4044694 100644 --- a/tests/function-returns/return-string-concat-var.choc +++ b/tests/function-returns/return-string-concat-var.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-string-concat-var | filecheck %s # RUN: python %s | filecheck %s # NOTE: Broken in executable diff --git a/tests/function-returns/return-string-concat.choc b/tests/function-returns/return-string-concat.choc index 9c78fd5..35e7081 100644 --- a/tests/function-returns/return-string-concat.choc +++ b/tests/function-returns/return-string-concat.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-string-concat | filecheck %s # RUN: python %s | filecheck %s # NOTE: Broken in executable diff --git a/tests/function-returns/return-string-index.choc b/tests/function-returns/return-string-index.choc index bd0f9a3..2716a54 100644 --- a/tests/function-returns/return-string-index.choc +++ b/tests/function-returns/return-string-index.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-string-index | filecheck %s # RUN: python %s | filecheck %s # NOTE: Broken in executable diff --git a/tests/function-returns/return-string-literal.choc b/tests/function-returns/return-string-literal.choc index b2aa3ba..3480989 100644 --- a/tests/function-returns/return-string-literal.choc +++ b/tests/function-returns/return-string-literal.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-string-literal | filecheck %s # RUN: python %s | filecheck %s a: str = "" diff --git a/tests/function-returns/return-string-var.choc b/tests/function-returns/return-string-var.choc index a622961..59c0dc6 100644 --- a/tests/function-returns/return-string-var.choc +++ b/tests/function-returns/return-string-var.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./return-string-var | filecheck %s # RUN: python %s | filecheck %s a: str = "" diff --git a/tests/lists/combine-lists.choc b/tests/lists/combine-lists.choc index 6cb3e7f..ad9b074 100644 --- a/tests/lists/combine-lists.choc +++ b/tests/lists/combine-lists.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./combine-lists | filecheck %s # RUN: python %s | filecheck %s a : [int] = None diff --git a/tests/lists/for-list.choc b/tests/lists/for-list.choc index e32566a..5e5ca83 100644 --- a/tests/lists/for-list.choc +++ b/tests/lists/for-list.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN ./for-list | filecheck %s # RUN: python %s | filecheck %s i: int = 0 diff --git a/tests/lists/for-none.choc b/tests/lists/for-none.choc index ed76ba6..9c822f8 100644 --- a/tests/lists/for-none.choc +++ b/tests/lists/for-none.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./for-none | filecheck %s i: int = 0 l: [int] = None diff --git a/tests/lists/list-index-nested.choc b/tests/lists/list-index-nested.choc index 352d25c..8154543 100644 --- a/tests/lists/list-index-nested.choc +++ b/tests/lists/list-index-nested.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-index-nested | filecheck %s # RUN: python %s | filecheck %s a : [[int]] = None diff --git a/tests/lists/list-index-oob-negative.choc b/tests/lists/list-index-oob-negative.choc index bfbd16b..28d7cb0 100644 --- a/tests/lists/list-index-oob-negative.choc +++ b/tests/lists/list-index-oob-negative.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-index-oob-negative | filecheck %s a : [int] = None diff --git a/tests/lists/list-index-oob.choc b/tests/lists/list-index-oob.choc index 3c8e262..a3e71bc 100644 --- a/tests/lists/list-index-oob.choc +++ b/tests/lists/list-index-oob.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-index-oob | filecheck %s a : [int] = None diff --git a/tests/lists/list-index.choc b/tests/lists/list-index.choc index fda654b..0986ae5 100644 --- a/tests/lists/list-index.choc +++ b/tests/lists/list-index.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-index | filecheck %s # RUN: python %s | filecheck %s a : [int] = None diff --git a/tests/lists/list-len.choc b/tests/lists/list-len.choc index 692234b..8d6974e 100644 --- a/tests/lists/list-len.choc +++ b/tests/lists/list-len.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-len | filecheck %s # RUN: python %s | filecheck %s a : [int] = None diff --git a/tests/lists/list-none-len.choc b/tests/lists/list-none-len.choc index 096d45e..2f1e963 100644 --- a/tests/lists/list-none-len.choc +++ b/tests/lists/list-none-len.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-none-len | filecheck %s a : [int] = None diff --git a/tests/lists/list-of-string.choc b/tests/lists/list-of-string.choc index b69a3ad..288e3a1 100644 --- a/tests/lists/list-of-string.choc +++ b/tests/lists/list-of-string.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./list-of-string | filecheck %s # RUN: python %s | filecheck %s digits: [str] = None diff --git a/tests/lists/none-index.choc b/tests/lists/none-index.choc index 9243260..c09a6b1 100644 --- a/tests/lists/none-index.choc +++ b/tests/lists/none-index.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./none-index | filecheck %s l: [int] = None diff --git a/tests/others/func-def-double.choc b/tests/others/func-def-double.choc index ae0d0fc..c2d736b 100644 --- a/tests/others/func-def-double.choc +++ b/tests/others/func-def-double.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./func-def-double | filecheck %s # RUN: python %s | filecheck %s g: str = "global" diff --git a/tests/others/if-elif-else.choc b/tests/others/if-elif-else.choc index b815117..5c6e2d8 100644 --- a/tests/others/if-elif-else.choc +++ b/tests/others/if-elif-else.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./if-elif-else | filecheck %s # RUN: python %s | filecheck %s def foo(): diff --git a/tests/others/ifexpr.choc b/tests/others/ifexpr.choc index 43a7846..c230986 100644 --- a/tests/others/ifexpr.choc +++ b/tests/others/ifexpr.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./ifexpr | filecheck %s # RUN: python %s | filecheck %s print(True if True else False) diff --git a/tests/others/local-str-def.choc b/tests/others/local-str-def.choc index a91ac84..5704d1c 100644 --- a/tests/others/local-str-def.choc +++ b/tests/others/local-str-def.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./local-str-def | filecheck %s # RUN: python %s | filecheck %s def foo(): diff --git a/tests/others/single-stmt-while.choc b/tests/others/single-stmt-while.choc index 5bba49d..837ef94 100644 --- a/tests/others/single-stmt-while.choc +++ b/tests/others/single-stmt-while.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-stmt-while | filecheck %s # RUN: python %s | filecheck %s cond: bool = True diff --git a/tests/pass.choc b/tests/pass.choc index 30aa285..a1260a3 100644 --- a/tests/pass.choc +++ b/tests/pass.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./pass | filecheck %s # RUN: python %s | filecheck %s pass diff --git a/tests/print-integer-literal.choc b/tests/print-integer-literal.choc index e152ab0..1db2c1b 100644 --- a/tests/print-integer-literal.choc +++ b/tests/print-integer-literal.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./print-integer-literal | filecheck %s # RUN: python %s | filecheck %s print(0) diff --git a/tests/strings/concat.choc b/tests/strings/concat.choc index d84ae0f..4d0b3ed 100644 --- a/tests/strings/concat.choc +++ b/tests/strings/concat.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./concat | filecheck %s # RUN: python %s | filecheck %s print("Hello " + "world!") diff --git a/tests/strings/equal.choc b/tests/strings/equal.choc index a102804..bfcbfdc 100644 --- a/tests/strings/equal.choc +++ b/tests/strings/equal.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./equal | filecheck %s # RUN: python %s | filecheck %s print("foo" == "foo") diff --git a/tests/strings/index.choc b/tests/strings/index.choc index e273652..ffe38f7 100644 --- a/tests/strings/index.choc +++ b/tests/strings/index.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./index | filecheck %s # RUN: python %s | filecheck %s a : str = "aaaaa" diff --git a/tests/strings/literals.choc b/tests/strings/literals.choc index f530421..16a3cbb 100644 --- a/tests/strings/literals.choc +++ b/tests/strings/literals.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./literals | filecheck %s # RUN: python %s | filecheck %s diff --git a/tests/strings/read-str.choc b/tests/strings/read-str.choc index 2a3727b..d7e6af1 100644 --- a/tests/strings/read-str.choc +++ b/tests/strings/read-str.choc @@ -1,4 +1,4 @@ -# RUN: echo "Hello-World" | lli %s.ll | filecheck %s +# RUN: echo "Hello-World" | ./read-str | filecheck %s # RUN: echo "Hello-World" | python %s | filecheck %s a : str = "" diff --git a/tests/strings/single-str-def.choc b/tests/strings/single-str-def.choc index 98031e8..0fe0490 100644 --- a/tests/strings/single-str-def.choc +++ b/tests/strings/single-str-def.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-str-def | filecheck %s # RUN: python %s | filecheck %s s: str = "foo" diff --git a/tests/strings/string-for-loop.choc b/tests/strings/string-for-loop.choc index 476c8ce..7710ab4 100644 --- a/tests/strings/string-for-loop.choc +++ b/tests/strings/string-for-loop.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./string-for-loop | filecheck %s # RUN: python %s | filecheck %s x: str = "" diff --git a/tests/var-defs/global-var.choc b/tests/var-defs/global-var.choc index 280e164..036327b 100644 --- a/tests/var-defs/global-var.choc +++ b/tests/var-defs/global-var.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./global-var | filecheck %s # RUN: python %s | filecheck %s x: int = 0 diff --git a/tests/var-defs/multi-assign-order.choc b/tests/var-defs/multi-assign-order.choc index 7f97e2d..8005591 100644 --- a/tests/var-defs/multi-assign-order.choc +++ b/tests/var-defs/multi-assign-order.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./multi-assign-order | filecheck %s # RUN: python %s | filecheck %s # The order of execution of the assignments is important here. diff --git a/tests/var-defs/multi-assign.choc b/tests/var-defs/multi-assign.choc index 9f27b46..c6edcd8 100644 --- a/tests/var-defs/multi-assign.choc +++ b/tests/var-defs/multi-assign.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./multi-assign | filecheck %s # RUN: python %s | filecheck %s i: int = 0 diff --git a/tests/var-defs/multiple-defs.choc b/tests/var-defs/multiple-defs.choc index ce1b43e..59386dc 100644 --- a/tests/var-defs/multiple-defs.choc +++ b/tests/var-defs/multiple-defs.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./multiple-defs | filecheck %s # RUN: python %s | filecheck %s i1: int = 2 diff --git a/tests/var-defs/rewrite-int-def.choc b/tests/var-defs/rewrite-int-def.choc index d5a0a5a..c748918 100644 --- a/tests/var-defs/rewrite-int-def.choc +++ b/tests/var-defs/rewrite-int-def.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./rewrite-int-def | filecheck %s # RUN: python %s | filecheck %s i: int = 2 diff --git a/tests/var-defs/single-bool-def.choc b/tests/var-defs/single-bool-def.choc index 21bfeec..363c2fa 100644 --- a/tests/var-defs/single-bool-def.choc +++ b/tests/var-defs/single-bool-def.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-bool-def | filecheck %s # RUN: python %s | filecheck %s x: bool = True diff --git a/tests/var-defs/single-int-def.choc b/tests/var-defs/single-int-def.choc index 9c6de1c..2b2147c 100644 --- a/tests/var-defs/single-int-def.choc +++ b/tests/var-defs/single-int-def.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./single-int-def | filecheck %s # RUN: python %s | filecheck %s i: int = 42 diff --git a/tests/var-defs/var-def-in-func.choc b/tests/var-defs/var-def-in-func.choc index 1512809..c9acdf8 100644 --- a/tests/var-defs/var-def-in-func.choc +++ b/tests/var-defs/var-def-in-func.choc @@ -1,4 +1,4 @@ -# RUN: lli %s.ll | filecheck %s +# RUN: ./var-def-in-func | filecheck %s # RUN: python %s | filecheck %s def foo() -> int: From 4f5bf02c3ffd40818e0a831d669e65a44f36b9cd Mon Sep 17 00:00:00 2001 From: ashiven Date: Wed, 17 Sep 2025 18:53:17 +0200 Subject: [PATCH 2/7] use malloc for strings - todo: fix memory leaks via free and determine func return static alloc vs dynamic alloc --- main.go | 2 +- pkg/codegen/codegen.go | 29 ++++++++++++++++++++++++++++- pkg/codegen/defvar.go | 7 ++++--- pkg/codegen/exprbinary.go | 18 ++++++++++++++++++ pkg/codegen/exprcall.go | 7 ++++++- pkg/codegen/functions.go | 14 ++++++++++++++ pkg/codegen/utilinstr.go | 2 ++ pkg/codegen/utilstrings.go | 11 +++++++++-- 8 files changed, 82 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index 9db8692..da47ae8 100644 --- a/main.go +++ b/main.go @@ -75,7 +75,7 @@ func main() { // TODO: To keep the test cases working I am only appending .ll to the filePath // here but will have to change that in the future and modify the test cases accordingly. err := os.WriteFile( - filePath+".ll", + replaceFileEnding(filePath, "ll"), []byte(codeGenerator.Module.String()), 0o644, ) diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index b93dc48..2586a4a 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -3,9 +3,11 @@ package codegen import ( - "chogopy/pkg/ast" "fmt" "strconv" + "strings" + + "chogopy/pkg/ast" "github.com/llir/llvm/ir" "github.com/llir/llvm/ir/constant" @@ -28,6 +30,7 @@ type VarInfo struct { name string elemType types.Type value value.Value + init constant.Constant } type ( @@ -47,6 +50,7 @@ type CodeGenerator struct { functions Functions varContext VarCtx + heapAllocs []value.Value mainFunction *ir.Func mainBlock *ir.Block @@ -71,6 +75,7 @@ func (cg *CodeGenerator) Generate(program *ast.Program) { cg.registerFuncs() cg.varContext = VarCtx{} + cg.heapAllocs = []value.Value{} cg.mainFunction = cg.Module.NewFunc("main", types.I32) cg.mainBlock = cg.mainFunction.NewBlock(cg.uniqueNames.get("entry")) @@ -92,6 +97,9 @@ func (cg *CodeGenerator) Generate(program *ast.Program) { statement.Visit(cg) } + // Add a free() for each call to malloc() at the end of the main function + // cg.freeHeap() + cg.currentBlock.NewRet(constant.NewInt(types.I32, 0)) } @@ -147,3 +155,22 @@ func (cg *CodeGenerator) setVar(varInfo VarInfo) { localVars[varInfo.name] = varInfo } } + +func (cg *CodeGenerator) freeHeap() { + for _, ptr := range cg.heapAllocs { + // First, we need to check whether the pointer SSA value is even defined in the current scope. + // This is relevant when a pointer is heap-allocated and then returned from a function, + // which results in the pointer SSA value being shadowed by the function return SSA value. + inScope := false + for _, inst := range cg.currentBlock.Insts { + if strings.Contains(inst.LLString(), ptr.Ident()) { + inScope = true + break + } + } + + if inScope { + cg.currentBlock.NewCall(cg.functions["free"], ptr) + } + } +} diff --git a/pkg/codegen/defvar.go b/pkg/codegen/defvar.go index 61cef56..93a445d 100644 --- a/pkg/codegen/defvar.go +++ b/pkg/codegen/defvar.go @@ -1,9 +1,10 @@ package codegen import ( - "chogopy/pkg/ast" "strings" + "chogopy/pkg/ast" + "github.com/llir/llvm/ir/constant" "github.com/llir/llvm/ir/types" ) @@ -16,14 +17,14 @@ func (cg *CodeGenerator) VisitVarDef(varDef *ast.VarDef) { case cg.mainFunction: globalVar := cg.Module.NewGlobalDef(varName, literalConst) cg.setVar( - VarInfo{name: varName, elemType: globalVar.Typ.ElemType, value: globalVar}, + VarInfo{name: varName, elemType: globalVar.Typ.ElemType, value: globalVar, init: literalConst}, ) default: localVar := cg.currentBlock.NewAlloca(literalConst.Type()) cg.currentBlock.NewStore(literalConst, localVar) cg.setVar( - VarInfo{name: varName, elemType: localVar.Typ.ElemType, value: localVar}, + VarInfo{name: varName, elemType: localVar.Typ.ElemType, value: localVar, init: literalConst}, ) } } diff --git a/pkg/codegen/exprbinary.go b/pkg/codegen/exprbinary.go index fe71dbd..d948609 100644 --- a/pkg/codegen/exprbinary.go +++ b/pkg/codegen/exprbinary.go @@ -122,3 +122,21 @@ func (cg *CodeGenerator) concat(binaryExpr *ast.BinaryExpr, lhs value.Value, rhs return false } + +// func (cg CodeGenerator) getStrLiteral(node ast.Node) string { +// if isIdentOrIndex(node) { +// varInfo, _ := cg.getVar(node.(*ast.IdentExpr).Identifier) +// initConst := varInfo.init.(*constant.ExprGetElementPtr) +// charArr := initConst.Src.(*ir.Global).Init.(*constant.CharArray).X +// strLiteral := string(charArr[:len(charArr)-1]) // Remove '/0' from the char array +// return strLiteral +// +// } else { +// return node.(*ast.LiteralExpr).Value.(string) +// } +// } + +// lhsString := cg.getStrLiteral(binaryExpr.Lhs) +// rhsString := cg.getStrLiteral(binaryExpr.Rhs) +// +// cg.lastGenerated = cg.concatStrings(lhsString, rhsString) diff --git a/pkg/codegen/exprcall.go b/pkg/codegen/exprcall.go index 2e23851..09454ae 100644 --- a/pkg/codegen/exprcall.go +++ b/pkg/codegen/exprcall.go @@ -1,9 +1,10 @@ package codegen import ( - "chogopy/pkg/ast" "log" + "chogopy/pkg/ast" + "github.com/llir/llvm/ir/types" "github.com/llir/llvm/ir/value" ) @@ -34,6 +35,10 @@ func (cg *CodeGenerator) VisitCallExpr(callExpr *ast.CallExpr) { callRes := cg.currentBlock.NewCall(callee, args...) callRes.LocalName = cg.uniqueNames.get("call_res") + if callRes.Type().Equal(types.I8Ptr) { + cg.heapAllocs = append(cg.heapAllocs, callRes) + } + cg.lastGenerated = callRes } diff --git a/pkg/codegen/functions.go b/pkg/codegen/functions.go index 60a05ee..5eb1c28 100644 --- a/pkg/codegen/functions.go +++ b/pkg/codegen/functions.go @@ -112,6 +112,18 @@ func (cg *CodeGenerator) registerExternal() { ) printf.Sig.Variadic = true + malloc := cg.Module.NewFunc( + "malloc", + types.I8Ptr, + ir.NewParam("", types.I32), + ) + + free := cg.Module.NewFunc( + "free", + types.Void, + ir.NewParam("", types.I8Ptr), + ) + //fgets := cg.Module.NewFunc( // "fgets", // types.I8Ptr, @@ -136,6 +148,8 @@ func (cg *CodeGenerator) registerExternal() { cg.functions["memcpy"] = memcpy cg.functions["sprintf"] = sprintf cg.functions["printf"] = printf + cg.functions["malloc"] = malloc + cg.functions["free"] = free // cg.functions["fgets"] = fgets // cg.functions["fdopen"] = fdopen } diff --git a/pkg/codegen/utilinstr.go b/pkg/codegen/utilinstr.go index 193d4e9..e6f3e1a 100644 --- a/pkg/codegen/utilinstr.go +++ b/pkg/codegen/utilinstr.go @@ -50,9 +50,11 @@ func (cg *CodeGenerator) NewLiteral(literal any) value.Value { zero := constant.NewInt(types.I32, 0) strConst := constant.NewGetElementPtr(charArrGlobal.Typ.ElemType, charArrGlobal, zero, zero) strPtr := cg.currentBlock.NewAlloca(types.I8Ptr) + strPtr.LocalName = cg.uniqueNames.get("str_ptr") cg.NewStore(strConst, strPtr) strLoad := cg.currentBlock.NewLoad(types.I8Ptr, strPtr) + strLoad.LocalName = cg.uniqueNames.get("str_literal") return strLoad diff --git a/pkg/codegen/utilstrings.go b/pkg/codegen/utilstrings.go index 9435f5c..eb97dcc 100644 --- a/pkg/codegen/utilstrings.go +++ b/pkg/codegen/utilstrings.go @@ -83,7 +83,8 @@ func (cg *CodeGenerator) clampString(strVal value.Value) value.Value { strLen := cg.currentBlock.NewCall(cg.functions["strlen"], strVal) strLen.LocalName = cg.uniqueNames.get("str_len") - copyBuffer := cg.NewAllocN(types.I8, strLen) + copyBuffer := cg.currentBlock.NewCall(cg.functions["malloc"], strLen) + cg.heapAllocs = append(cg.heapAllocs, copyBuffer) copyBuffer.LocalName = cg.uniqueNames.get("clamp_buf_ptr") copyRes := cg.currentBlock.NewCall(cg.functions["strcpy"], copyBuffer, strVal) copyRes.LocalName = cg.uniqueNames.get("clamp_copy_res") @@ -105,8 +106,9 @@ func (cg *CodeGenerator) concatStrings(lhs value.Value, rhs value.Value) value.V concatLen := cg.currentBlock.NewAdd(lhsLen, rhsLen) concatLen = cg.currentBlock.NewAdd(concatLen, constant.NewInt(types.I32, 1)) concatLen.LocalName = cg.uniqueNames.get("concat_len") - concatStr := cg.NewAllocN(types.I8, concatLen) + concatStr := cg.currentBlock.NewCall(cg.functions["malloc"], concatLen) concatStr.LocalName = cg.uniqueNames.get("concat_str") + cg.heapAllocs = append(cg.heapAllocs, concatStr) // 2) Copy the string that should be appended to into that buffer copyRes := cg.currentBlock.NewCall(cg.functions["strcpy"], concatStr, lhs) @@ -118,3 +120,8 @@ func (cg *CodeGenerator) concatStrings(lhs value.Value, rhs value.Value) value.V return concatRes } + +// func (cg *CodeGenerator) concatStrings(lhs string, rhs string) value.Value { +// concatStr := cg.NewLiteral(lhs + rhs) +// return concatStr +// } From b417e793e36ccadc8a5f5daff11911f640d9e7ec Mon Sep 17 00:00:00 2001 From: ashiven Date: Thu, 18 Sep 2025 00:42:17 +0200 Subject: [PATCH 3/7] move strings to the heap for local allocs --- pkg/codegen/codegen.go | 2 +- pkg/codegen/defvar.go | 17 +++++++++++++++++ pkg/codegen/utilinstr.go | 22 ++++++++++++++-------- pkg/codegen/utillists.go | 10 ++++++---- pkg/codegen/utilstrings.go | 4 +++- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index 2586a4a..c2bb7c4 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -98,7 +98,7 @@ func (cg *CodeGenerator) Generate(program *ast.Program) { } // Add a free() for each call to malloc() at the end of the main function - // cg.freeHeap() + cg.freeHeap() cg.currentBlock.NewRet(constant.NewInt(types.I32, 0)) } diff --git a/pkg/codegen/defvar.go b/pkg/codegen/defvar.go index 93a445d..5354a8c 100644 --- a/pkg/codegen/defvar.go +++ b/pkg/codegen/defvar.go @@ -22,7 +22,24 @@ func (cg *CodeGenerator) VisitVarDef(varDef *ast.VarDef) { default: localVar := cg.currentBlock.NewAlloca(literalConst.Type()) + localVar.LocalName = cg.uniqueNames.get("local_var") cg.currentBlock.NewStore(literalConst, localVar) + + // Move string literal to heap + if literalConst.Type().Equal(types.I8Ptr) { + // Store static string into stack-allocated string ptr + strStack := cg.currentBlock.NewLoad(types.I8Ptr, localVar) + strStack.LocalName = cg.uniqueNames.get("str_stack") + + // Copy string into heap-allocated string ptr + strLiteral := varDef.Literal.(*ast.LiteralExpr).Value.(string) + strHeap := cg.currentBlock.NewCall(cg.functions["malloc"], constant.NewInt(types.I32, int64(len(strLiteral)+1))) + strHeap.LocalName = cg.uniqueNames.get("str_heap") + strCopy := cg.currentBlock.NewCall(cg.functions["sprintf"], strHeap, cg.strings["str_format"], strStack) + strCopy.LocalName = cg.uniqueNames.get("strcpy_res") + cg.currentBlock.NewStore(strHeap, localVar) + } + cg.setVar( VarInfo{name: varName, elemType: localVar.Typ.ElemType, value: localVar, init: literalConst}, ) diff --git a/pkg/codegen/utilinstr.go b/pkg/codegen/utilinstr.go index e6f3e1a..e987df4 100644 --- a/pkg/codegen/utilinstr.go +++ b/pkg/codegen/utilinstr.go @@ -47,16 +47,22 @@ func (cg *CodeGenerator) NewLiteral(literal any) value.Value { charArrConst := constant.NewCharArrayFromString(literal + "\x00") charArrGlobal := cg.Module.NewGlobalDef(cg.uniqueNames.get("str"), charArrConst) + // Store static string into stack-allocated string ptr zero := constant.NewInt(types.I32, 0) strConst := constant.NewGetElementPtr(charArrGlobal.Typ.ElemType, charArrGlobal, zero, zero) - strPtr := cg.currentBlock.NewAlloca(types.I8Ptr) - strPtr.LocalName = cg.uniqueNames.get("str_ptr") - - cg.NewStore(strConst, strPtr) - strLoad := cg.currentBlock.NewLoad(types.I8Ptr, strPtr) - strLoad.LocalName = cg.uniqueNames.get("str_literal") - - return strLoad + strPtrStack := cg.currentBlock.NewAlloca(types.I8Ptr) + strPtrStack.LocalName = cg.uniqueNames.get("str_ptr_stack") + cg.NewStore(strConst, strPtrStack) + strStack := cg.currentBlock.NewLoad(types.I8Ptr, strPtrStack) + strStack.LocalName = cg.uniqueNames.get("str_stack") + + // Copy string into heap-allocated string ptr + strHeap := cg.currentBlock.NewCall(cg.functions["malloc"], constant.NewInt(types.I32, int64(len(literal)+1))) + strHeap.LocalName = cg.uniqueNames.get("str_heap") + strCopy := cg.currentBlock.NewCall(cg.functions["sprintf"], strHeap, cg.strings["str_format"], strStack) + strCopy.LocalName = cg.uniqueNames.get("strcpy_res") + + return strHeap case nil: noneConst := constant.NewNull(types.NewPointer(cg.types["none"])) diff --git a/pkg/codegen/utillists.go b/pkg/codegen/utillists.go index 771661e..c1e08eb 100644 --- a/pkg/codegen/utillists.go +++ b/pkg/codegen/utillists.go @@ -74,7 +74,7 @@ func (cg *CodeGenerator) setListContent(list value.Value, content value.Value) { cg.NewStore(content, listContentAddr) } -// TODO: use global allocation +// TODO: use heap allocation func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType types.Type) value.Value { zero := constant.NewInt(types.I32, 0) four := constant.NewInt(types.I32, 4) @@ -96,18 +96,20 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType concatInit := constant.NewBool(true) concatListElemType := getListElemTypeFromListType(listType) - concatContentPtr := cg.NewAllocN(concatListElemType, concatLen) + concatContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], concatLen) + concatContentPtr.LocalName = cg.uniqueNames.get("concat_content_ptr") + cg.heapAllocs = append(cg.heapAllocs, concatContentPtr) cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtr, lhsContentPtr, lhsLenByte) if isList(concatContentPtr) { /* Content is another list */ contentIdx := constant.NewInt(types.I32, 0) - concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatContentPtr.ElemType, concatContentPtr, lhsLen, contentIdx) + concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen, contentIdx) cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) } else { /* Regular list content */ - concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatContentPtr.ElemType, concatContentPtr, lhsLen) + concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen) cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) } diff --git a/pkg/codegen/utilstrings.go b/pkg/codegen/utilstrings.go index eb97dcc..976432a 100644 --- a/pkg/codegen/utilstrings.go +++ b/pkg/codegen/utilstrings.go @@ -84,6 +84,7 @@ func (cg *CodeGenerator) clampString(strVal value.Value) value.Value { strLen := cg.currentBlock.NewCall(cg.functions["strlen"], strVal) strLen.LocalName = cg.uniqueNames.get("str_len") copyBuffer := cg.currentBlock.NewCall(cg.functions["malloc"], strLen) + copyBuffer.LocalName = cg.uniqueNames.get("copy_buffer") cg.heapAllocs = append(cg.heapAllocs, copyBuffer) copyBuffer.LocalName = cg.uniqueNames.get("clamp_buf_ptr") copyRes := cg.currentBlock.NewCall(cg.functions["strcpy"], copyBuffer, strVal) @@ -96,7 +97,8 @@ func (cg *CodeGenerator) clampString(strVal value.Value) value.Value { return copyBuffer } -// TODO: use global allocation +// TODO: maybe switch to malloc for string literals so I don't have +// to differentiate between statically allocated strings and heap allocated strings for function returns func (cg *CodeGenerator) concatStrings(lhs value.Value, rhs value.Value) value.Value { // 1) Allocate a destination buffer of size: char[lhsLen + rhsLen + 1] (one more for the zero byte) lhsLen := cg.currentBlock.NewCall(cg.functions["strlen"], lhs) From b7031665e011d359a1fca37c0c792ff0c80a1f5e Mon Sep 17 00:00:00 2001 From: ashiven Date: Thu, 18 Sep 2025 01:01:17 +0200 Subject: [PATCH 4/7] fix input alloc --- pkg/codegen/exprcall.go | 3 +++ pkg/codegen/functions.go | 12 ++++++------ pkg/codegen/utilstrings.go | 2 -- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/codegen/exprcall.go b/pkg/codegen/exprcall.go index 09454ae..8f966e1 100644 --- a/pkg/codegen/exprcall.go +++ b/pkg/codegen/exprcall.go @@ -5,6 +5,7 @@ import ( "chogopy/pkg/ast" + "github.com/llir/llvm/ir" "github.com/llir/llvm/ir/types" "github.com/llir/llvm/ir/value" ) @@ -23,10 +24,12 @@ func (cg *CodeGenerator) VisitCallExpr(callExpr *ast.CallExpr) { switch callExpr.FuncName { case "len": lenRes := cg.getLen(args[0]) + lenRes.(*ir.InstCall).LocalName = cg.uniqueNames.get("call_res") cg.lastGenerated = lenRes return case "print": printRes := cg.printGeneric(args[0]) + printRes.(*ir.InstCall).LocalName = cg.uniqueNames.get("call_res") cg.lastGenerated = printRes return } diff --git a/pkg/codegen/functions.go b/pkg/codegen/functions.go index 5eb1c28..3488b72 100644 --- a/pkg/codegen/functions.go +++ b/pkg/codegen/functions.go @@ -7,6 +7,8 @@ import ( "github.com/llir/llvm/ir/types" ) +var MaxBufferSize = int64(1000) + func (cg *CodeGenerator) registerFuncs() { cg.addStringConstants() cg.registerExternal() @@ -185,15 +187,13 @@ func (cg *CodeGenerator) defineInput() *ir.Func { strFormatPtr := cg.useStringDef(funcBlock, "str_format") - inputPtr := funcBlock.NewAlloca(types.NewArray(MaxBufferSize, types.I8)) - inputPtr.LocalName = cg.uniqueNames.get("input_ptr") - inputCast := funcBlock.NewBitCast(inputPtr, types.I8Ptr) - inputCast.LocalName = cg.uniqueNames.get("input_ptr_cast") + inputStr := funcBlock.NewCall(cg.functions["malloc"], constant.NewInt(types.I32, MaxBufferSize)) + inputStr.LocalName = cg.uniqueNames.get("input_ptr") - scanRes := funcBlock.NewCall(cg.functions["scanf"], strFormatPtr, inputCast) + scanRes := funcBlock.NewCall(cg.functions["scanf"], strFormatPtr, inputStr) scanRes.LocalName = cg.uniqueNames.get("scan_res") - funcBlock.NewRet(inputCast) + funcBlock.NewRet(inputStr) return input } diff --git a/pkg/codegen/utilstrings.go b/pkg/codegen/utilstrings.go index 976432a..bce3c3d 100644 --- a/pkg/codegen/utilstrings.go +++ b/pkg/codegen/utilstrings.go @@ -7,8 +7,6 @@ import ( "github.com/llir/llvm/ir/value" ) -var MaxBufferSize = uint64(10000) - // isString returns true if the value is a // - char array: [n x i8] // - string: i8* From 4441f1c5f2cee9cdf5de27a9b04dda0221f2078f Mon Sep 17 00:00:00 2001 From: ashiven Date: Thu, 18 Sep 2025 02:28:08 +0200 Subject: [PATCH 5/7] add heap allocation for list structs - still need to add corresponding frees --- pkg/codegen/functions.go | 10 +++---- pkg/codegen/utillists.go | 54 +++++++++++++++++++++++++++++--------- pkg/codegen/utilstrings.go | 6 ++--- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/pkg/codegen/functions.go b/pkg/codegen/functions.go index 3488b72..af2f8f9 100644 --- a/pkg/codegen/functions.go +++ b/pkg/codegen/functions.go @@ -454,11 +454,11 @@ func (cg *CodeGenerator) definePrintInt() *ir.Func { func (cg *CodeGenerator) definePrintBool() *ir.Func { arg := ir.NewParam("", types.I1) - boolToStr := cg.Module.NewFunc("booltostr", types.I32, arg) + printBool := cg.Module.NewFunc("printbool", types.I32, arg) - entry := boolToStr.NewBlock(cg.uniqueNames.get("entry")) - ifBlock := boolToStr.NewBlock(cg.uniqueNames.get("booltostr.then")) - elseBlock := boolToStr.NewBlock(cg.uniqueNames.get("booltostr.else")) + entry := printBool.NewBlock(cg.uniqueNames.get("entry")) + ifBlock := printBool.NewBlock(cg.uniqueNames.get("printbool.then")) + elseBlock := printBool.NewBlock(cg.uniqueNames.get("printbool.else")) entry.NewCondBr(arg, ifBlock, elseBlock) @@ -470,7 +470,7 @@ func (cg *CodeGenerator) definePrintBool() *ir.Func { falsePrint := elseBlock.NewCall(cg.functions["printf"], falseStr) elseBlock.NewRet(falsePrint) - return boolToStr + return printBool } // NOTE: We can't use the below function because it uses alloca to diff --git a/pkg/codegen/utillists.go b/pkg/codegen/utillists.go index c1e08eb..507c85f 100644 --- a/pkg/codegen/utillists.go +++ b/pkg/codegen/utillists.go @@ -74,49 +74,77 @@ func (cg *CodeGenerator) setListContent(list value.Value, content value.Value) { cg.NewStore(content, listContentAddr) } -// TODO: use heap allocation +// TODO: move this into its own function to avoid code repetition func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType types.Type) value.Value { zero := constant.NewInt(types.I32, 0) four := constant.NewInt(types.I32, 4) + // Compute lhs list content pointer and length (word-to-byte-adjusted) lhsContentPtr := cg.getListElemPtr(lhs, zero) lhsLenFunc := lhs.Type().(*types.PointerType).ElemType.Name() + "_len" lhsLen := cg.currentBlock.NewCall(cg.functions[lhsLenFunc], lhs) - // TODO: We are multiplying the list lengths by four because memcpy expects a length in bytes (i8) rather than words (i32). - // However, if we are concatenating nested lists, we may need to adjust these lengths differently. + lhsLen.LocalName = cg.uniqueNames.get("lhs_len_word") lhsLenByte := cg.currentBlock.NewMul(lhsLen, four) + lhsLenByte.LocalName = cg.uniqueNames.get("lhs_len_byte") + // Compute rhs list content pointer and length (word-to-byte-adjusted) rhsContentPtr := cg.getListElemPtr(rhs, zero) rhsLenFunc := rhs.Type().(*types.PointerType).ElemType.Name() + "_len" rhsLen := cg.currentBlock.NewCall(cg.functions[rhsLenFunc], rhs) + rhsLen.LocalName = cg.uniqueNames.get("rhs_len_word") rhsLenByte := cg.currentBlock.NewMul(rhsLen, four) - - concatPtr := cg.currentBlock.NewAlloca(listType) + rhsLenByte.LocalName = cg.uniqueNames.get("rhs_len_byte") + + // Trick to get the size of a list struct for malloc + listTypeSize := cg.currentBlock.NewGetElementPtr(listType, constant.NewNull(types.NewPointer(listType)), constant.NewInt(types.I32, 1)) + listTypeSize.LocalName = cg.uniqueNames.get("list_size_ptr") + listSizeInt := cg.currentBlock.NewPtrToInt(listTypeSize, types.I32) + listSizeInt.LocalName = cg.uniqueNames.get("list_size_int") + + // Heap-allocation for the list struct + concatPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listSizeInt) + concatPtr.LocalName = cg.uniqueNames.get("concat_ptr") + concatPtrCast := cg.currentBlock.NewBitCast(concatPtr, types.NewPointer(listType)) + concatPtrCast.LocalName = cg.uniqueNames.get("concat_ptr_cast") + cg.heapAllocs = append(cg.heapAllocs, concatPtrCast) + + // Initial values for list init and length concatLen := cg.currentBlock.NewAdd(lhsLen, rhsLen) + concatLen.LocalName = cg.uniqueNames.get("concat_len_word") + concatLenByte := cg.currentBlock.NewAdd(lhsLenByte, rhsLenByte) + concatLenByte.LocalName = cg.uniqueNames.get("concat_len_byte") concatInit := constant.NewBool(true) + // Heap-allocation for the list content concatListElemType := getListElemTypeFromListType(listType) - concatContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], concatLen) + concatContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], concatLenByte) concatContentPtr.LocalName = cg.uniqueNames.get("concat_content_ptr") cg.heapAllocs = append(cg.heapAllocs, concatContentPtr) - cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtr, lhsContentPtr, lhsLenByte) + + // Copy lhs into concat content and then rhs into shifted concat content ptr + lhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtr, lhsContentPtr, lhsLenByte) + lhsCpyRes.LocalName = cg.uniqueNames.get("lhs_cpy_res") if isList(concatContentPtr) { /* Content is another list */ contentIdx := constant.NewInt(types.I32, 0) concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen, contentIdx) - cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) + concatContentPtrShifted.LocalName = cg.uniqueNames.get("concat_content_ptr_shifted") + rhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) + rhsCpyRes.LocalName = cg.uniqueNames.get("rhs_cpy_res") } else { /* Regular list content */ concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen) - cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) + concatContentPtrShifted.LocalName = cg.uniqueNames.get("concat_content_ptr_shifted") + rhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) + rhsCpyRes.LocalName = cg.uniqueNames.get("rhs_cpy_res") } - cg.setListLen(concatPtr, concatLen) - cg.setListInit(concatPtr, concatInit) - cg.setListContent(concatPtr, concatContentPtr) - return concatPtr + cg.setListLen(concatPtrCast, concatLen) + cg.setListInit(concatPtrCast, concatInit) + cg.setListContent(concatPtrCast, concatContentPtr) + return concatPtrCast } // newList dynamically allocates and returns a pointer to a list literal diff --git a/pkg/codegen/utilstrings.go b/pkg/codegen/utilstrings.go index bce3c3d..19581a0 100644 --- a/pkg/codegen/utilstrings.go +++ b/pkg/codegen/utilstrings.go @@ -82,9 +82,8 @@ func (cg *CodeGenerator) clampString(strVal value.Value) value.Value { strLen := cg.currentBlock.NewCall(cg.functions["strlen"], strVal) strLen.LocalName = cg.uniqueNames.get("str_len") copyBuffer := cg.currentBlock.NewCall(cg.functions["malloc"], strLen) - copyBuffer.LocalName = cg.uniqueNames.get("copy_buffer") - cg.heapAllocs = append(cg.heapAllocs, copyBuffer) copyBuffer.LocalName = cg.uniqueNames.get("clamp_buf_ptr") + cg.heapAllocs = append(cg.heapAllocs, copyBuffer) copyRes := cg.currentBlock.NewCall(cg.functions["strcpy"], copyBuffer, strVal) copyRes.LocalName = cg.uniqueNames.get("clamp_copy_res") @@ -95,8 +94,7 @@ func (cg *CodeGenerator) clampString(strVal value.Value) value.Value { return copyBuffer } -// TODO: maybe switch to malloc for string literals so I don't have -// to differentiate between statically allocated strings and heap allocated strings for function returns +// TODO: move this into its own function to avoid code repetition func (cg *CodeGenerator) concatStrings(lhs value.Value, rhs value.Value) value.Value { // 1) Allocate a destination buffer of size: char[lhsLen + rhsLen + 1] (one more for the zero byte) lhsLen := cg.currentBlock.NewCall(cg.functions["strlen"], lhs) From fe79e068b8d438ef5b4148d535113ad57b12e7a0 Mon Sep 17 00:00:00 2001 From: ashiven Date: Thu, 18 Sep 2025 04:08:31 +0200 Subject: [PATCH 6/7] implement heap alloc for list literals --- pkg/codegen/defvar.go | 1 + pkg/codegen/exprcall.go | 2 +- pkg/codegen/exprlist.go | 78 ++++++++++++++++++++-------------------- pkg/codegen/utillists.go | 45 +++++++++++++++-------- 4 files changed, 72 insertions(+), 54 deletions(-) diff --git a/pkg/codegen/defvar.go b/pkg/codegen/defvar.go index 5354a8c..c4e5606 100644 --- a/pkg/codegen/defvar.go +++ b/pkg/codegen/defvar.go @@ -13,6 +13,7 @@ func (cg *CodeGenerator) VisitVarDef(varDef *ast.VarDef) { varName := varDef.TypedVar.(*ast.TypedVar).VarName literalConst := cg.getLiteralConst(varDef) + // TODO: double free for function returning global pointer to static? switch cg.currentFunction { case cg.mainFunction: globalVar := cg.Module.NewGlobalDef(varName, literalConst) diff --git a/pkg/codegen/exprcall.go b/pkg/codegen/exprcall.go index 8f966e1..7b8b47d 100644 --- a/pkg/codegen/exprcall.go +++ b/pkg/codegen/exprcall.go @@ -38,7 +38,7 @@ func (cg *CodeGenerator) VisitCallExpr(callExpr *ast.CallExpr) { callRes := cg.currentBlock.NewCall(callee, args...) callRes.LocalName = cg.uniqueNames.get("call_res") - if callRes.Type().Equal(types.I8Ptr) { + if _, ok := callRes.Type().(*types.PointerType); ok { cg.heapAllocs = append(cg.heapAllocs, callRes) } diff --git a/pkg/codegen/exprlist.go b/pkg/codegen/exprlist.go index 03540be..2e7b3ef 100644 --- a/pkg/codegen/exprlist.go +++ b/pkg/codegen/exprlist.go @@ -9,46 +9,11 @@ import ( ) func (cg *CodeGenerator) VisitListExpr(listExpr *ast.ListExpr) { - listPtr := cg.newStaticList(listExpr) + listPtr := cg.newDynamicList(listExpr) - cg.lastGenerated = listPtr -} - -// newDynamicList allocates memory for a list expression on the currently -// executing functions' call stack and returns a pointer to this memory. -// This method should be used carefully because it may lead to dangling pointers -// if a function returns a list expression allocated in this way. -func (cg *CodeGenerator) newDynamicList(listExpr *ast.ListExpr) value.Value { - // attrToType will return something like: - // - // list{content: i32*, size: i32, init: i1}* - // - // - // So we want to take out the elemType: - // - // list{content: i32*, size: i32, init: i1} - // - // - // In order for the allocation (listPtr := cg.newList(...)) to have the correct type: - // - // list{content: i32*, size: i32, init: i1}* - listType := cg.attrToType(listExpr.TypeHint).(*types.PointerType).ElemType - - listElems := []value.Value{} - for _, elem := range listExpr.Elements { - elem.Visit(cg) - elemVal := cg.lastGenerated + // TODO: copy list to heap and return that instead - if isIdentOrIndex(elem) { - elemVal = cg.LoadVal(elemVal) - } - - listElems = append(listElems, elemVal) - } - - listPtr := cg.newList(listElems, listType) - - return listPtr + cg.lastGenerated = listPtr } // newConstantList assumes that all list literals will contain nothing but @@ -124,3 +89,40 @@ func (cg *CodeGenerator) getStaticListElems(listExpr *ast.ListExpr) []constant.C return listElems } + +// newDynamicList allocates memory for a list expression on the currently +// executing functions' call stack and returns a pointer to this memory. +// This method should be used carefully because it may lead to dangling pointers +// if a function returns a list expression allocated in this way. +func (cg *CodeGenerator) newDynamicList(listExpr *ast.ListExpr) value.Value { + // attrToType will return something like: + // + // list{content: i32*, size: i32, init: i1}* + // + // + // So we want to take out the elemType: + // + // list{content: i32*, size: i32, init: i1} + // + // + // In order for the allocation (listPtr := cg.newList(...)) to have the correct type: + // + // list{content: i32*, size: i32, init: i1}* + listType := cg.attrToType(listExpr.TypeHint).(*types.PointerType).ElemType + + listElems := []value.Value{} + for _, elem := range listExpr.Elements { + elem.Visit(cg) + elemVal := cg.lastGenerated + + if isIdentOrIndex(elem) { + elemVal = cg.LoadVal(elemVal) + } + + listElems = append(listElems, elemVal) + } + + listPtr := cg.newList(listElems, listType) + + return listPtr +} diff --git a/pkg/codegen/utillists.go b/pkg/codegen/utillists.go index 507c85f..2c44f41 100644 --- a/pkg/codegen/utillists.go +++ b/pkg/codegen/utillists.go @@ -119,23 +119,25 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType concatListElemType := getListElemTypeFromListType(listType) concatContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], concatLenByte) concatContentPtr.LocalName = cg.uniqueNames.get("concat_content_ptr") - cg.heapAllocs = append(cg.heapAllocs, concatContentPtr) + concatContentPtrCast := cg.currentBlock.NewBitCast(concatContentPtr, types.NewPointer(concatListElemType)) + concatContentPtrCast.LocalName = cg.uniqueNames.get("concat_content_ptr") + cg.heapAllocs = append(cg.heapAllocs, concatContentPtrCast) // Copy lhs into concat content and then rhs into shifted concat content ptr - lhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtr, lhsContentPtr, lhsLenByte) + lhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrCast, lhsContentPtr, lhsLenByte) lhsCpyRes.LocalName = cg.uniqueNames.get("lhs_cpy_res") - if isList(concatContentPtr) { + if isList(concatContentPtrCast) { /* Content is another list */ contentIdx := constant.NewInt(types.I32, 0) - concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen, contentIdx) + concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtrCast, lhsLen, contentIdx) concatContentPtrShifted.LocalName = cg.uniqueNames.get("concat_content_ptr_shifted") rhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) rhsCpyRes.LocalName = cg.uniqueNames.get("rhs_cpy_res") } else { /* Regular list content */ - concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtr, lhsLen) + concatContentPtrShifted := cg.currentBlock.NewGetElementPtr(concatListElemType, concatContentPtrCast, lhsLen) concatContentPtrShifted.LocalName = cg.uniqueNames.get("concat_content_ptr_shifted") rhsCpyRes := cg.currentBlock.NewCall(cg.functions["memcpy"], concatContentPtrShifted, rhsContentPtr, rhsLenByte) rhsCpyRes.LocalName = cg.uniqueNames.get("rhs_cpy_res") @@ -143,7 +145,7 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType cg.setListLen(concatPtrCast, concatLen) cg.setListInit(concatPtrCast, concatInit) - cg.setListContent(concatPtrCast, concatContentPtr) + cg.setListContent(concatPtrCast, concatContentPtrCast) return concatPtrCast } @@ -155,39 +157,52 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType func (cg *CodeGenerator) newList(listElems []value.Value, listType types.Type) value.Value { /* list.size and list.init */ listLen := constant.NewInt(types.I32, int64(len(listElems))) + listLenByte := constant.NewInt(types.I32, int64(len(listElems)*4)) listInit := constant.NewBool(true) /* list.content alloc */ listElemType := getListElemTypeFromListType(listType) - listContentPtr := cg.NewAllocN(listElemType, listLen) + listContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listLenByte) listContentPtr.LocalName = cg.uniqueNames.get("list_content_ptr") + listContentPtrCast := cg.currentBlock.NewBitCast(listContentPtr, types.NewPointer(listElemType)) + listContentPtrCast.LocalName = cg.uniqueNames.get("list_content_ptr_cast") + cg.heapAllocs = append(cg.heapAllocs, listContentPtrCast) /* list.content store */ for elemIdx, elem := range listElems { elemIdx := constant.NewInt(types.I32, int64(elemIdx)) - if isList(listContentPtr) { + if isList(listContentPtrCast) { /* Content is another list */ contentIdx := constant.NewInt(types.I32, 0) - elemAddr := cg.currentBlock.NewGetElementPtr(listElemType, listContentPtr, elemIdx, contentIdx) + elemAddr := cg.currentBlock.NewGetElementPtr(listElemType, listContentPtrCast, elemIdx, contentIdx) elemAddr.LocalName = cg.uniqueNames.get("list_content_elem_addr") cg.NewStore(elem, elemAddr) } else { /* Regular list content */ - elemAddr := cg.currentBlock.NewGetElementPtr(listElemType, listContentPtr, elemIdx) + elemAddr := cg.currentBlock.NewGetElementPtr(listElemType, listContentPtrCast, elemIdx) elemAddr.LocalName = cg.uniqueNames.get("list_content_elem_addr") cg.NewStore(elem, elemAddr) } } + /* trick to get the size of a list struct for malloc */ + listTypeSize := cg.currentBlock.NewGetElementPtr(listType, constant.NewNull(types.NewPointer(listType)), constant.NewInt(types.I32, 1)) + listTypeSize.LocalName = cg.uniqueNames.get("list_size_ptr") + listSizeInt := cg.currentBlock.NewPtrToInt(listTypeSize, types.I32) + listSizeInt.LocalName = cg.uniqueNames.get("list_size_int") + /* list alloc */ - listPtr := cg.currentBlock.NewAlloca(listType) + listPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listSizeInt) listPtr.LocalName = cg.uniqueNames.get("list_ptr") + listPtrCast := cg.currentBlock.NewBitCast(listPtr, types.NewPointer(listType)) + listPtrCast.LocalName = cg.uniqueNames.get("list_ptr_cast") + cg.heapAllocs = append(cg.heapAllocs, listPtrCast) /* list store */ - cg.setListLen(listPtr, listLen) - cg.setListInit(listPtr, listInit) - cg.setListContent(listPtr, listContentPtr) - return listPtr + cg.setListLen(listPtrCast, listLen) + cg.setListInit(listPtrCast, listInit) + cg.setListContent(listPtrCast, listContentPtrCast) + return listPtrCast } From d131cd0310a356acd4de20e31dc5e6a36e8036b6 Mon Sep 17 00:00:00 2001 From: ashiven Date: Thu, 18 Sep 2025 04:43:34 +0200 Subject: [PATCH 7/7] fix lists of string --- pkg/codegen/defvar.go | 1 + pkg/codegen/exprlist.go | 3 --- pkg/codegen/utillists.go | 29 +++++++++++++---------------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pkg/codegen/defvar.go b/pkg/codegen/defvar.go index c4e5606..0e5f026 100644 --- a/pkg/codegen/defvar.go +++ b/pkg/codegen/defvar.go @@ -36,6 +36,7 @@ func (cg *CodeGenerator) VisitVarDef(varDef *ast.VarDef) { strLiteral := varDef.Literal.(*ast.LiteralExpr).Value.(string) strHeap := cg.currentBlock.NewCall(cg.functions["malloc"], constant.NewInt(types.I32, int64(len(strLiteral)+1))) strHeap.LocalName = cg.uniqueNames.get("str_heap") + cg.heapAllocs = append(cg.heapAllocs, strHeap) strCopy := cg.currentBlock.NewCall(cg.functions["sprintf"], strHeap, cg.strings["str_format"], strStack) strCopy.LocalName = cg.uniqueNames.get("strcpy_res") cg.currentBlock.NewStore(strHeap, localVar) diff --git a/pkg/codegen/exprlist.go b/pkg/codegen/exprlist.go index 2e7b3ef..4937e2b 100644 --- a/pkg/codegen/exprlist.go +++ b/pkg/codegen/exprlist.go @@ -10,9 +10,6 @@ import ( func (cg *CodeGenerator) VisitListExpr(listExpr *ast.ListExpr) { listPtr := cg.newDynamicList(listExpr) - - // TODO: copy list to heap and return that instead - cg.lastGenerated = listPtr } diff --git a/pkg/codegen/utillists.go b/pkg/codegen/utillists.go index 2c44f41..6f6e70b 100644 --- a/pkg/codegen/utillists.go +++ b/pkg/codegen/utillists.go @@ -95,14 +95,8 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType rhsLenByte := cg.currentBlock.NewMul(rhsLen, four) rhsLenByte.LocalName = cg.uniqueNames.get("rhs_len_byte") - // Trick to get the size of a list struct for malloc - listTypeSize := cg.currentBlock.NewGetElementPtr(listType, constant.NewNull(types.NewPointer(listType)), constant.NewInt(types.I32, 1)) - listTypeSize.LocalName = cg.uniqueNames.get("list_size_ptr") - listSizeInt := cg.currentBlock.NewPtrToInt(listTypeSize, types.I32) - listSizeInt.LocalName = cg.uniqueNames.get("list_size_int") - // Heap-allocation for the list struct - concatPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listSizeInt) + concatPtr := cg.currentBlock.NewCall(cg.functions["malloc"], cg.sizeof(listType, 1)) concatPtr.LocalName = cg.uniqueNames.get("concat_ptr") concatPtrCast := cg.currentBlock.NewBitCast(concatPtr, types.NewPointer(listType)) concatPtrCast.LocalName = cg.uniqueNames.get("concat_ptr_cast") @@ -157,12 +151,11 @@ func (cg *CodeGenerator) concatLists(lhs value.Value, rhs value.Value, listType func (cg *CodeGenerator) newList(listElems []value.Value, listType types.Type) value.Value { /* list.size and list.init */ listLen := constant.NewInt(types.I32, int64(len(listElems))) - listLenByte := constant.NewInt(types.I32, int64(len(listElems)*4)) listInit := constant.NewBool(true) /* list.content alloc */ listElemType := getListElemTypeFromListType(listType) - listContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listLenByte) + listContentPtr := cg.currentBlock.NewCall(cg.functions["malloc"], cg.sizeof(listElemType, int64(len(listElems)))) listContentPtr.LocalName = cg.uniqueNames.get("list_content_ptr") listContentPtrCast := cg.currentBlock.NewBitCast(listContentPtr, types.NewPointer(listElemType)) listContentPtrCast.LocalName = cg.uniqueNames.get("list_content_ptr_cast") @@ -187,14 +180,8 @@ func (cg *CodeGenerator) newList(listElems []value.Value, listType types.Type) v } } - /* trick to get the size of a list struct for malloc */ - listTypeSize := cg.currentBlock.NewGetElementPtr(listType, constant.NewNull(types.NewPointer(listType)), constant.NewInt(types.I32, 1)) - listTypeSize.LocalName = cg.uniqueNames.get("list_size_ptr") - listSizeInt := cg.currentBlock.NewPtrToInt(listTypeSize, types.I32) - listSizeInt.LocalName = cg.uniqueNames.get("list_size_int") - /* list alloc */ - listPtr := cg.currentBlock.NewCall(cg.functions["malloc"], listSizeInt) + listPtr := cg.currentBlock.NewCall(cg.functions["malloc"], cg.sizeof(listType, 1)) listPtr.LocalName = cg.uniqueNames.get("list_ptr") listPtrCast := cg.currentBlock.NewBitCast(listPtr, types.NewPointer(listType)) listPtrCast.LocalName = cg.uniqueNames.get("list_ptr_cast") @@ -206,3 +193,13 @@ func (cg *CodeGenerator) newList(listElems []value.Value, listType types.Type) v cg.setListContent(listPtrCast, listContentPtrCast) return listPtrCast } + +func (cg *CodeGenerator) sizeof(type_ types.Type, multiplier int64) value.Value { + typeSize := cg.currentBlock.NewGetElementPtr(type_, constant.NewNull(types.NewPointer(type_)), constant.NewInt(types.I32, 1)) + typeSize.LocalName = cg.uniqueNames.get("type_size_ptr") + typeSizeInt := cg.currentBlock.NewPtrToInt(typeSize, types.I32) + typeSizeInt.LocalName = cg.uniqueNames.get("type_size_int") + typeSizeMult := cg.currentBlock.NewMul(typeSizeInt, constant.NewInt(types.I32, multiplier)) + typeSizeMult.LocalName = cg.uniqueNames.get("type_size_mul") + return typeSizeMult +}