Merge branch 'master' into structs

# Conflicts:
#	compiler/src/prog8/compiler/Compiler.kt
This commit is contained in:
Irmen de Jong
2025-05-30 12:39:23 +02:00
7 changed files with 108 additions and 46 deletions

View File

@@ -65,6 +65,7 @@ private fun compileMain(args: Array<String>): 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 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 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 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 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 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") 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<String>): Boolean {
warnSymbolShadowing == true, warnSymbolShadowing == true,
quietAll == true, quietAll == true,
quietAll == true || quietAssembler == true, quietAll == true || quietAssembler == true,
showTimings == true,
asmListfile == true, asmListfile == true,
dontIncludeSourcelines != true, dontIncludeSourcelines != true,
experimentalCodegen == true, experimentalCodegen == true,
@@ -265,6 +267,7 @@ private fun compileMain(args: Array<String>): Boolean {
warnSymbolShadowing == true, warnSymbolShadowing == true,
quietAll == true, quietAll == true,
quietAll == true || quietAssembler == true, quietAll == true || quietAssembler == true,
showTimings == true,
asmListfile == true, asmListfile == true,
dontIncludeSourcelines != true, dontIncludeSourcelines != true,
experimentalCodegen == true, experimentalCodegen == true,

View File

@@ -24,9 +24,10 @@ import java.nio.file.Path
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.isRegularFile import kotlin.io.path.isRegularFile
import kotlin.io.path.nameWithoutExtension import kotlin.io.path.nameWithoutExtension
import kotlin.math.round
import kotlin.system.exitProcess 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 class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
@@ -40,6 +41,7 @@ class CompilerArguments(val filepath: Path,
val warnSymbolShadowing: Boolean, val warnSymbolShadowing: Boolean,
val quietAll: Boolean, val quietAll: Boolean,
val quietAssembler: Boolean, val quietAssembler: Boolean,
val showTimings: Boolean,
val asmListfile: Boolean, val asmListfile: Boolean,
val includeSourcelines: Boolean, val includeSourcelines: Boolean,
val experimentalCodegen: Boolean, val experimentalCodegen: Boolean,
@@ -83,9 +85,20 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
} }
try { try {
val totalTime = measureTimeMillis { val totalTime = measureTime {
val libraryDirs = if(compTarget.libraryPath!=null) listOf(compTarget.libraryPath.toString()) else emptyList() 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 compilationOptions = options
with(compilationOptions) { with(compilationOptions) {
@@ -120,10 +133,14 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
} }
val processDuration = measureTime {
processAst(program, args.errors, compilationOptions) processAst(program, args.errors, compilationOptions)
}
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************") // println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
// printProgram(program) // printProgram(program)
val optimizeDuration = measureTime {
if (compilationOptions.optimize) { if (compilationOptions.optimize) {
optimizeAst( optimizeAst(
program, program,
@@ -132,26 +149,33 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
BuiltinFunctionsFacade(BuiltinFunctions), BuiltinFunctionsFacade(BuiltinFunctions),
) )
} }
}
val postprocessDuration = measureTime {
determineProgramLoadAddress(program, compilationOptions, args.errors) determineProgramLoadAddress(program, compilationOptions, args.errors)
args.errors.report() args.errors.report()
postprocessAst(program, args.errors, compilationOptions) postprocessAst(program, args.errors, compilationOptions)
args.errors.report() args.errors.report()
}
// println("*********** COMPILER AST BEFORE ASSEMBLYGEN *************") // println("*********** COMPILER AST BEFORE ASSEMBLYGEN *************")
// printProgram(program) // printProgram(program)
var createAssemblyDuration = Duration.ZERO
var simplifiedAstDuration = Duration.ZERO
if (args.writeAssembly) { if (args.writeAssembly) {
// re-initialize memory areas with final compilationOptions // re-initialize memory areas with final compilationOptions
compilationOptions.compTarget.initializeMemoryAreas(compilationOptions) compilationOptions.compTarget.initializeMemoryAreas(compilationOptions)
if(args.printAst1) { if (args.printAst1) {
println("\n*********** COMPILER AST *************") println("\n*********** COMPILER AST *************")
printProgram(program) printProgram(program)
println("*********** COMPILER AST END *************\n") println("*********** COMPILER AST END *************\n")
} }
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform() val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions) val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
val symbolTable = stMaker.make() val symbolTable = stMaker.make()
@@ -159,12 +183,12 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors) postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
args.errors.report() args.errors.report()
if(compilationOptions.optimize) { if (compilationOptions.optimize) {
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors) optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
args.errors.report() args.errors.report()
} }
if(args.printAst2) { if (args.printAst2) {
println("\n*********** SIMPLIFIED AST *************") println("\n*********** SIMPLIFIED AST *************")
printAst(intermediateAst, true, ::println) printAst(intermediateAst, true, ::println)
println("*********** SIMPLIFIED AST END *************\n") println("*********** SIMPLIFIED AST END *************\n")
@@ -172,29 +196,51 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors) verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
args.errors.report() args.errors.report()
intermediateAst
}
simplifiedAstDuration =simplifiedAstDuration2
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions, program.generatedLabelSequenceNumber)) { createAssemblyDuration = measureTime {
if (!createAssemblyAndAssemble(
intermediateAst,
args.errors,
compilationOptions,
program.generatedLabelSequenceNumber
)
) {
System.err.println("Error in codegeneration or assembler") System.err.println("Error in codegeneration or assembler")
return null return null
} }
}
ast = intermediateAst ast = intermediateAst
} else { } else {
if(args.printAst1) { if (args.printAst1) {
println("\n*********** COMPILER AST *************") println("\n*********** COMPILER AST *************")
printProgram(program) printProgram(program)
println("*********** COMPILER AST END *************\n") 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.err.println("There is no simplified Ast available if assembly generation is disabled.")
} }
} }
}
System.out.flush() System.out.flush()
System.err.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}")
}
}
if(!args.quietAll) { if(!args.quietAll) {
val seconds = totalTime / 1000.0 println("\nTotal compilation+assemble time: ${totalTime}.")
println("\nTotal compilation+assemble time: ${round(seconds * 100.0) / 100.0} sec.")
} }
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles) return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
} catch (px: ParseError) { } catch (px: ParseError) {

View File

@@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
warnSymbolShadowing = false, warnSymbolShadowing = false,
quietAll = true, quietAll = true,
quietAssembler = true, quietAssembler = true,
showTimings = false,
asmListfile = false, asmListfile = false,
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,

View File

@@ -28,6 +28,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
warnSymbolShadowing = false, warnSymbolShadowing = false,
quietAll = true, quietAll = true,
quietAssembler = true, quietAssembler = true,
showTimings = false,
asmListfile = false, asmListfile = false,
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,

View File

@@ -27,6 +27,7 @@ internal fun compileFile(
warnSymbolShadowing = false, warnSymbolShadowing = false,
quietAll = true, quietAll = true,
quietAssembler = true, quietAssembler = true,
showTimings = false,
asmListfile = false, asmListfile = false,
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,

View File

@@ -18,14 +18,20 @@ Then you can choose a few ways to get a compiler:
**Or, install via a Package Manager:** **Or, install via a Package Manager:**
Currently, it's only available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems. Arch Linux:
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_. Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems.
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_.
This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``. 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 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 <https://aur.archlinux.org/packages/tass64>`_ In case you prefer to install AUR packages in a traditional manner, make sure to install `"tass64" package <https://aur.archlinux.org/packages/tass64>`_
before installing prog8, as `makepkg <https://wiki.archlinux.org/title/Makepkg>`_ itself doesn't fetch AUR dependencies. before installing prog8, as `makepkg <https://wiki.archlinux.org/title/Makepkg>`_ itself doesn't fetch AUR dependencies.
Mac OS (and Linux, and WSL2 on Windows):
Prog8 can be installed via `Homebrew <https://formulae.brew.sh/formula/prog8>`_ 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:** **Or, download a bleeding edge development version from Github:**
@@ -236,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. 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. 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`` ``-varsgolden``
Like ``-varshigh``, but places the variables in the $0400-$07FF "golden ram" area instead. 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. Because this is in normal system memory, there are no bank switching issues.

View File

@@ -139,6 +139,7 @@ Libraries
Optimizations 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) - 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" - 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? - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?