From ec50b5a007dc8a52ceb56c6d459d5d46d10427ff Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 30 May 2025 03:06:04 +0200 Subject: [PATCH 1/2] homebrew info --- docs/source/compiling.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/source/compiling.rst b/docs/source/compiling.rst index ed1c00cd7..f060bda48 100644 --- a/docs/source/compiling.rst +++ b/docs/source/compiling.rst @@ -18,14 +18,20 @@ Then you can choose a few ways to get a compiler: **Or, install via a Package Manager:** -Currently, it's only available on `AUR `_ for Arch Linux and compatible systems. -The package is called `"prog8" `_. +Arch Linux: + Currently, it's available on `AUR `_ for Arch Linux and compatible systems. + The package is called `"prog8" `_. -This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``. -In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method. + This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``. + In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method. -In case you prefer to install AUR packages in a traditional manner, make sure to install `"tass64" package `_ -before installing prog8, as `makepkg `_ itself doesn't fetch AUR dependencies. + In case you prefer to install AUR packages in a traditional manner, make sure to install `"tass64" package `_ + before installing prog8, as `makepkg `_ itself doesn't fetch AUR dependencies. + +Mac OS (and Linux, and WSL2 on Windows): + Prog8 can be installed via `Homebrew `_ using the command ``brew install prog8``. + It will make the ``prog8c`` command available and also installs the other required software tools for you. + While Homebrew works on Linux, it's probably best to first check your distribution's native package manager. **Or, download a bleeding edge development version from Github:** From 341778ba6787d789894386070a077bbdf6d11ebf Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 30 May 2025 12:38:16 +0200 Subject: [PATCH 2/2] added -timings flag --- compiler/src/prog8/CompilerMain.kt | 3 + compiler/src/prog8/compiler/Compiler.kt | 132 ++++++++++++++------- compiler/test/TestCompilerOnExamples.kt | 1 + compiler/test/TestCompilerOptionLibdirs.kt | 1 + compiler/test/helpers/compileXyz.kt | 1 + docs/source/compiling.rst | 3 + docs/source/todo.rst | 1 + 7 files changed, 99 insertions(+), 43 deletions(-) diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 09debc911..7e442e1c5 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -65,6 +65,7 @@ private fun compileMain(args: Array): Boolean { val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit") val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator) val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)") + val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)") val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.") val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.") val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM") @@ -181,6 +182,7 @@ private fun compileMain(args: Array): Boolean { warnSymbolShadowing == true, quietAll == true, quietAll == true || quietAssembler == true, + showTimings == true, asmListfile == true, dontIncludeSourcelines != true, experimentalCodegen == true, @@ -265,6 +267,7 @@ private fun compileMain(args: Array): Boolean { warnSymbolShadowing == true, quietAll == true, quietAll == true || quietAssembler == true, + showTimings == true, asmListfile == true, dontIncludeSourcelines != true, experimentalCodegen == true, diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index e507e134d..8226db9bd 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -24,9 +24,10 @@ import java.nio.file.Path import kotlin.io.path.Path import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension -import kotlin.math.round import kotlin.system.exitProcess -import kotlin.system.measureTimeMillis +import kotlin.time.Duration +import kotlin.time.measureTime +import kotlin.time.measureTimedValue class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead @@ -40,6 +41,7 @@ class CompilerArguments(val filepath: Path, val warnSymbolShadowing: Boolean, val quietAll: Boolean, val quietAssembler: Boolean, + val showTimings: Boolean, val asmListfile: Boolean, val includeSourcelines: Boolean, val experimentalCodegen: Boolean, @@ -83,9 +85,20 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { } try { - val totalTime = measureTimeMillis { + val totalTime = measureTime { val libraryDirs = if(compTarget.libraryPath!=null) listOf(compTarget.libraryPath.toString()) else emptyList() - val (program, options, imported) = parseMainModule(args.filepath, args.errors, compTarget, args.sourceDirs, libraryDirs, args.quietAll) + val (parseresult, parseDuration) = measureTimedValue { + parseMainModule( + args.filepath, + args.errors, + compTarget, + args.sourceDirs, + libraryDirs, + args.quietAll + ) + } + + val (program, options, imported) = parseresult compilationOptions = options with(compilationOptions) { @@ -120,81 +133,114 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { } - processAst(program, args.errors, compilationOptions) + val processDuration = measureTime { + processAst(program, args.errors, compilationOptions) + } + // println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************") // printProgram(program) - if (compilationOptions.optimize) { - optimizeAst( - program, - compilationOptions, - args.errors, - BuiltinFunctionsFacade(BuiltinFunctions), - ) + val optimizeDuration = measureTime { + if (compilationOptions.optimize) { + optimizeAst( + program, + compilationOptions, + args.errors, + BuiltinFunctionsFacade(BuiltinFunctions), + ) + } } - determineProgramLoadAddress(program, compilationOptions, args.errors) - args.errors.report() - postprocessAst(program, args.errors, compilationOptions) - args.errors.report() + val postprocessDuration = measureTime { + determineProgramLoadAddress(program, compilationOptions, args.errors) + args.errors.report() + postprocessAst(program, args.errors, compilationOptions) + args.errors.report() + } // println("*********** COMPILER AST BEFORE ASSEMBLYGEN *************") // printProgram(program) + var createAssemblyDuration = Duration.ZERO + var simplifiedAstDuration = Duration.ZERO + if (args.writeAssembly) { // re-initialize memory areas with final compilationOptions compilationOptions.compTarget.initializeMemoryAreas(compilationOptions) - if(args.printAst1) { + if (args.printAst1) { println("\n*********** COMPILER AST *************") printProgram(program) println("*********** COMPILER AST END *************\n") } - val intermediateAst = SimplifiedAstMaker(program, args.errors).transform() - val stMaker = SymbolTableMaker(intermediateAst, compilationOptions) - val symbolTable = stMaker.make() + val (intermediateAst, simplifiedAstDuration2) = measureTimedValue { + val intermediateAst = SimplifiedAstMaker(program, args.errors).transform() + val stMaker = SymbolTableMaker(intermediateAst, compilationOptions) + val symbolTable = stMaker.make() - postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors) - args.errors.report() - - if(compilationOptions.optimize) { - optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors) + postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors) args.errors.report() + + if (compilationOptions.optimize) { + optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors) + args.errors.report() + } + + if (args.printAst2) { + println("\n*********** SIMPLIFIED AST *************") + printAst(intermediateAst, true, ::println) + println("*********** SIMPLIFIED AST END *************\n") + } + + verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors) + args.errors.report() + intermediateAst } + simplifiedAstDuration =simplifiedAstDuration2 - if(args.printAst2) { - println("\n*********** SIMPLIFIED AST *************") - printAst(intermediateAst, true, ::println) - println("*********** SIMPLIFIED AST END *************\n") - } - - verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors) - args.errors.report() - - if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions, program.generatedLabelSequenceNumber)) { - System.err.println("Error in codegeneration or assembler") - return null + createAssemblyDuration = measureTime { + if (!createAssemblyAndAssemble( + intermediateAst, + args.errors, + compilationOptions, + program.generatedLabelSequenceNumber + ) + ) { + System.err.println("Error in codegeneration or assembler") + return null + } } ast = intermediateAst } else { - if(args.printAst1) { + if (args.printAst1) { println("\n*********** COMPILER AST *************") printProgram(program) println("*********** COMPILER AST END *************\n") } - if(args.printAst2) { + if (args.printAst2) { System.err.println("There is no simplified Ast available if assembly generation is disabled.") } } + + System.out.flush() + System.err.flush() + + if(!args.quietAll && args.showTimings) { + println("\n**** TIMING ****") + println("source parsing : ${parseDuration}") + println("ast processing : ${processDuration}") + println("ast optimizing : ${optimizeDuration}") + println("ast postprocess : ${postprocessDuration}") + println("code prepare : ${simplifiedAstDuration}") + println("code generation : ${createAssemblyDuration}") + println(" total : ${parseDuration + processDuration + optimizeDuration + postprocessDuration + simplifiedAstDuration + createAssemblyDuration}") + } } - System.out.flush() - System.err.flush() if(!args.quietAll) { - val seconds = totalTime / 1000.0 - println("\nTotal compilation+assemble time: ${round(seconds * 100.0) / 100.0} sec.") + println("\nTotal compilation+assemble time: ${totalTime}.") } return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles) } catch (px: ParseError) { diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 0a797453b..53c94f52f 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat warnSymbolShadowing = false, quietAll = true, quietAssembler = true, + showTimings = false, asmListfile = false, includeSourcelines = false, experimentalCodegen = false, diff --git a/compiler/test/TestCompilerOptionLibdirs.kt b/compiler/test/TestCompilerOptionLibdirs.kt index fe67c7b62..54a3c93cc 100644 --- a/compiler/test/TestCompilerOptionLibdirs.kt +++ b/compiler/test/TestCompilerOptionLibdirs.kt @@ -28,6 +28,7 @@ class TestCompilerOptionSourcedirs: FunSpec({ warnSymbolShadowing = false, quietAll = true, quietAssembler = true, + showTimings = false, asmListfile = false, includeSourcelines = false, experimentalCodegen = false, diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index 4d557c9a6..6b979c00e 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -27,6 +27,7 @@ internal fun compileFile( warnSymbolShadowing = false, quietAll = true, quietAssembler = true, + showTimings = false, asmListfile = false, includeSourcelines = false, experimentalCodegen = false, diff --git a/docs/source/compiling.rst b/docs/source/compiling.rst index f060bda48..a7e18e5ce 100644 --- a/docs/source/compiling.rst +++ b/docs/source/compiling.rst @@ -242,6 +242,9 @@ One or more .p8 module files machine's configuration and properties from that configuration file instead of using one of the built-in targets. See :ref:`customizable_target` for details about this. +``-timings`` + Show a more detailed breakdown of the time taken in various compiler phases, for performance analysis of the compiler itself. + ``-varsgolden`` Like ``-varshigh``, but places the variables in the $0400-$07FF "golden ram" area instead. Because this is in normal system memory, there are no bank switching issues. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 687054019..3b5029575 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -71,6 +71,7 @@ Libraries Optimizations ------------- +- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time - Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples) - Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression" - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?