mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
IR example metrics now count instructions not p8ir lines
This commit is contained in:
1 parent
331e2b8f2b
commit
10ce992b35
9 files changed
+108
-42
No files matched your search
@@ -17,6 +17,10 @@ internal class AssemblyProgram(
|
||||
outputDir: Path,
|
||||
private val compTarget: ICompilationTarget) : IAssemblyProgram {
|
||||
|
||||
override val irInstructionCount: Int = 0
|
||||
override val irChunkCount: Int = 0
|
||||
override val irRegisterCount: Int = 0
|
||||
|
||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||
|
||||
@@ -34,6 +34,9 @@ class ExperiCodeGen(val retainSSA: Boolean): ICodeGeneratorBackend {
|
||||
|
||||
private object EmptyProgram : IAssemblyProgram {
|
||||
override val name = "<Empty Program>"
|
||||
override val irInstructionCount: Int = 0
|
||||
override val irChunkCount: Int = 0
|
||||
override val irRegisterCount: Int = 0
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
if(!options.quiet)
|
||||
println("** nothing assembled **")
|
||||
|
||||
@@ -32,6 +32,15 @@ internal class VmAssemblyProgram(
|
||||
internal val irProgram: IRProgram
|
||||
): IAssemblyProgram {
|
||||
|
||||
override val irInstructionCount: Int
|
||||
get() = irProgram.countCodeElements().first
|
||||
|
||||
override val irChunkCount: Int
|
||||
get() = irProgram.countCodeElements().second
|
||||
|
||||
override val irRegisterCount: Int
|
||||
get() = irProgram.countUsedRegisters()
|
||||
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
// the VM reads the IR file from disk.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
@@ -34,11 +34,17 @@ import kotlin.time.measureTime
|
||||
import kotlin.time.measureTimedValue
|
||||
|
||||
|
||||
private data class AssemblyResult(val success: Boolean, val irInstructionCount: Int, val irChunkCount: Int, val irRegisterCount: Int)
|
||||
|
||||
|
||||
class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
|
||||
val codegenAst: PtProgram?,
|
||||
val codegenSymboltable: SymbolTable?,
|
||||
val compilationOptions: CompilationOptions,
|
||||
val importedFiles: List<Path>)
|
||||
val importedFiles: List<Path>,
|
||||
val irInstructionCount: Int = 0,
|
||||
val irChunkCount: Int = 0,
|
||||
val irRegisterCount: Int = 0)
|
||||
|
||||
class CompilerArguments(val filepath: Path,
|
||||
val optimize: Boolean,
|
||||
@@ -76,6 +82,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
var ast: PtProgram? = null
|
||||
var resultingProgram: Program? = null
|
||||
var importedFiles: List<Path>
|
||||
var irInstructionCount = 0
|
||||
var irChunkCount = 0
|
||||
var irRegisterCount = 0
|
||||
|
||||
val targetConfigFile = expandTilde(Path(args.compilationTarget))
|
||||
val compTarget = if(targetConfigFile.isRegularFile()) {
|
||||
@@ -216,7 +225,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable!!, args.errors)
|
||||
args.errors.report()
|
||||
symbolTable = stMaker.make() // need an updated ST because the optimization changes stuff
|
||||
// symbolTable = stMaker.make() // need an updated ST because the optimization changes stuff
|
||||
}
|
||||
|
||||
// Remove redundant pointer typecasts - must run AFTER optimization, BEFORE code generation
|
||||
@@ -243,18 +252,23 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
simplifiedAstDuration = simplifiedAstDuration2
|
||||
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
val result = createAssemblyAndAssemble(
|
||||
intermediateAst,
|
||||
symbolTable!!,
|
||||
args.errors,
|
||||
compilationOptions,
|
||||
program.generatedLabelSequenceNumber
|
||||
)
|
||||
) {
|
||||
irInstructionCount = result.irInstructionCount
|
||||
irChunkCount = result.irChunkCount
|
||||
irRegisterCount = result.irRegisterCount
|
||||
if (!result.success) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
}
|
||||
}
|
||||
if (irInstructionCount < 0) {
|
||||
return null
|
||||
}
|
||||
ast = intermediateAst
|
||||
} else {
|
||||
if (args.printAst1) {
|
||||
@@ -286,7 +300,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
if(!args.quietAll) {
|
||||
println("\nTotal compilation+assemble time: ${totalTime.toString(DurationUnit.SECONDS, 3)}.")
|
||||
}
|
||||
return CompilationResult(resultingProgram!!, ast, symbolTable, compilationOptions, importedFiles)
|
||||
return CompilationResult(resultingProgram!!, ast, symbolTable, compilationOptions, importedFiles, irInstructionCount, irChunkCount, irRegisterCount)
|
||||
} catch (px: ParseError) {
|
||||
args.errors.printSingleError("ERROR ${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
} catch (ac: ErrorsReportedException) {
|
||||
@@ -617,7 +631,7 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
errors: IErrorReporter,
|
||||
compilerOptions: CompilationOptions,
|
||||
lastGeneratedLabelSequenceNr: Int
|
||||
): Boolean {
|
||||
): AssemblyResult {
|
||||
|
||||
val retainSSAforIR = true
|
||||
|
||||
@@ -633,9 +647,14 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
||||
errors.report()
|
||||
|
||||
return if(assembly!=null && errors.noErrors()) {
|
||||
val instructionCount = assembly?.irInstructionCount ?: 0
|
||||
val chunkCount = assembly?.irChunkCount ?: 0
|
||||
val registerCount = assembly?.irRegisterCount ?: 0
|
||||
|
||||
val success = if(assembly!=null && errors.noErrors()) {
|
||||
assembly.assemble(compilerOptions, errors)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
return AssemblyResult(success, instructionCount, chunkCount, registerCount)
|
||||
}
|
||||
@@ -616,7 +616,7 @@ _after:
|
||||
assignIndex = null
|
||||
conditionVar = null
|
||||
} else {
|
||||
conditionVar = VarDecl.createAuto(indexDt)
|
||||
conditionVar = VarDecl.createAuto(indexDt, ongoto.index.position)
|
||||
indexValue = IdentifierReference(listOf(conditionVar.name), conditionVar.position)
|
||||
val varTarget = AssignTarget(indexValue, null, null, null, false, position=conditionVar.position)
|
||||
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
|
||||
|
||||
@@ -37,8 +37,10 @@ private data class ExampleSizes(
|
||||
val cx16SizeUnoptimized: Int = 0,
|
||||
val pet32SizeOptimized: Int = 0,
|
||||
val pet32SizeUnoptimized: Int = 0,
|
||||
val virtualLineCountOptimized: Int = 0,
|
||||
val virtualLineCountUnoptimized: Int = 0
|
||||
val virtualInstrCountOptimized: Int = 0,
|
||||
val virtualInstrCountUnoptimized: Int = 0,
|
||||
val virtualRegCountOptimized: Int = 0,
|
||||
val virtualRegCountUnoptimized: Int = 0
|
||||
)
|
||||
|
||||
private val examplesDir = assumeDirectory(workingDir, "../examples")
|
||||
@@ -112,16 +114,23 @@ private fun verifyOutputFileSize(result: CompilationResult, expectedSize: Int) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the line count of the compiled .p8ir file against an expected value.
|
||||
* This is used for the virtual target where file size can be unstable due to non-deterministic formatting.
|
||||
* A small tolerance of ±5 lines is allowed for minor non-deterministic variations.
|
||||
* Verifies the IR instruction count of the compiled .p8ir file against an expected value.
|
||||
* This is a stable metric that doesn't change based on source position comments.
|
||||
* A small tolerance of ±5 instructions is allowed for minor non-deterministic variations.
|
||||
*/
|
||||
private fun verifyOutputFileLineCount(result: CompilationResult, expectedLineCount: Int) {
|
||||
val outputFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
val lineCount = Files.lines(outputFile).count().toInt()
|
||||
val tolerance = 2 // Allow ±2 lines for minor non-deterministic variations
|
||||
if (lineCount < expectedLineCount - tolerance || lineCount > expectedLineCount + tolerance) {
|
||||
lineCount shouldBe expectedLineCount
|
||||
private fun verifyIrInstructionCount(result: CompilationResult, expectedCount: Int) {
|
||||
val actualCount = result.irInstructionCount
|
||||
val tolerance = 5 // Allow ±5 instructions for minor non-deterministic variations
|
||||
if (actualCount < expectedCount - tolerance || actualCount > expectedCount + tolerance) {
|
||||
actualCount shouldBe expectedCount
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyIrRegisterCount(result: CompilationResult, expectedCount: Int) {
|
||||
val actualCount = result.irRegisterCount
|
||||
val tolerance = 5 // Allow ±5 registers for minor non-deterministic variations
|
||||
if (actualCount < expectedCount - tolerance || actualCount > expectedCount + tolerance) {
|
||||
actualCount shouldBe expectedCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,11 +301,11 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
ExampleSizes("swirl-float", c64SizeOptimized=708, c64SizeUnoptimized=735, cx16SizeOptimized=595, cx16SizeUnoptimized=906),
|
||||
ExampleSizes("tehtriz", c64SizeOptimized=4339, c64SizeUnoptimized=4504, cx16SizeOptimized=6215, cx16SizeUnoptimized=6747),
|
||||
ExampleSizes("textelite", c64SizeOptimized=11455, c64SizeUnoptimized=12778, cx16SizeOptimized=10789, cx16SizeUnoptimized=13505),
|
||||
ExampleSizes("pointers/animalgame", c64SizeOptimized=1570, c64SizeUnoptimized=2208, cx16SizeOptimized=1605, cx16SizeUnoptimized=2492, virtualLineCountOptimized=774, virtualLineCountUnoptimized=7598),
|
||||
ExampleSizes("pointers/binarytree", c64SizeOptimized=2562, c64SizeUnoptimized=2640, cx16SizeOptimized=2085, cx16SizeUnoptimized=2389, virtualLineCountOptimized=1705, virtualLineCountUnoptimized=4922),
|
||||
ExampleSizes("pointers/hashtable", c64SizeOptimized=3294, c64SizeUnoptimized=4266, cx16SizeOptimized=2832, cx16SizeUnoptimized=3866, virtualLineCountOptimized=1928, virtualLineCountUnoptimized=8066),
|
||||
ExampleSizes("pointers/sortedlist", c64SizeOptimized=1274, c64SizeUnoptimized=1319, cx16SizeOptimized=1223, cx16SizeUnoptimized=1532, virtualLineCountOptimized=953, virtualLineCountUnoptimized=5814),
|
||||
ExampleSizes("pointers/sorting", c64SizeOptimized=1776, c64SizeUnoptimized=2802, cx16SizeOptimized=1788, cx16SizeUnoptimized=2739, virtualLineCountOptimized=1369, virtualLineCountUnoptimized=8337),
|
||||
ExampleSizes("pointers/animalgame", c64SizeOptimized=1570, c64SizeUnoptimized=2208, cx16SizeOptimized=1605, cx16SizeUnoptimized=2492),
|
||||
ExampleSizes("pointers/binarytree", c64SizeOptimized=2562, c64SizeUnoptimized=2640, cx16SizeOptimized=2085, cx16SizeUnoptimized=2389),
|
||||
ExampleSizes("pointers/hashtable", c64SizeOptimized=3294, c64SizeUnoptimized=4266, cx16SizeOptimized=2832, cx16SizeUnoptimized=3866),
|
||||
ExampleSizes("pointers/sortedlist", c64SizeOptimized=1274, c64SizeUnoptimized=1319, cx16SizeOptimized=1223, cx16SizeUnoptimized=1532),
|
||||
ExampleSizes("pointers/sorting", c64SizeOptimized=1776, c64SizeUnoptimized=2802, cx16SizeOptimized=1788, cx16SizeUnoptimized=2739),
|
||||
),
|
||||
listOf(false, true),
|
||||
listOf(C64Target(), Cx16Target())
|
||||
@@ -332,17 +341,17 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
|
||||
val onlyVirtual = cartesianProduct(
|
||||
listOf(
|
||||
ExampleSizes("bouncegfx", virtualLineCountOptimized=916, virtualLineCountUnoptimized=3604),
|
||||
ExampleSizes("bsieve", virtualLineCountOptimized=828, virtualLineCountUnoptimized=4546),
|
||||
ExampleSizes("fountain", virtualLineCountOptimized=746, virtualLineCountUnoptimized=3449),
|
||||
ExampleSizes("pixelshader", virtualLineCountOptimized=390, virtualLineCountUnoptimized=1260),
|
||||
ExampleSizes("sincos", virtualLineCountOptimized=1103, virtualLineCountUnoptimized=3654),
|
||||
ExampleSizes("pointers/animalgame", virtualLineCountOptimized=894, virtualLineCountUnoptimized=8629),
|
||||
ExampleSizes("pointers/binarytree", virtualLineCountOptimized=2023, virtualLineCountUnoptimized=5732),
|
||||
ExampleSizes("pointers/hashtable", virtualLineCountOptimized=2267, virtualLineCountUnoptimized=9178),
|
||||
ExampleSizes("pointers/sortedlist", virtualLineCountOptimized=1117, virtualLineCountUnoptimized=6713),
|
||||
ExampleSizes("pointers/fountain-virtual", virtualLineCountOptimized=786, virtualLineCountUnoptimized=3491),
|
||||
ExampleSizes("pointers/sorting", virtualLineCountOptimized=1548, virtualLineCountUnoptimized=9401)
|
||||
ExampleSizes("bouncegfx", virtualInstrCountOptimized=208, virtualInstrCountUnoptimized=883, virtualRegCountOptimized=100, virtualRegCountUnoptimized=396),
|
||||
ExampleSizes("bsieve", virtualInstrCountOptimized=158, virtualInstrCountUnoptimized=1141, virtualRegCountOptimized=66, virtualRegCountUnoptimized=468),
|
||||
ExampleSizes("fountain", virtualInstrCountOptimized=152, virtualInstrCountUnoptimized=829, virtualRegCountOptimized=77, virtualRegCountUnoptimized=371),
|
||||
ExampleSizes("pixelshader", virtualInstrCountOptimized=43, virtualInstrCountUnoptimized=291, virtualRegCountOptimized=15, virtualRegCountUnoptimized=109),
|
||||
ExampleSizes("sincos", virtualInstrCountOptimized=276, virtualInstrCountUnoptimized=935, virtualRegCountOptimized=133, virtualRegCountUnoptimized=411),
|
||||
ExampleSizes("pointers/animalgame", virtualInstrCountOptimized=221, virtualInstrCountUnoptimized=2167, virtualRegCountOptimized=103, virtualRegCountUnoptimized=943),
|
||||
ExampleSizes("pointers/binarytree", virtualInstrCountOptimized=510, virtualInstrCountUnoptimized=1455, virtualRegCountOptimized=251, virtualRegCountUnoptimized=628),
|
||||
ExampleSizes("pointers/hashtable", virtualInstrCountOptimized=578, virtualInstrCountUnoptimized=2349, virtualRegCountOptimized=293, virtualRegCountUnoptimized=1055),
|
||||
ExampleSizes("pointers/sortedlist", virtualInstrCountOptimized=267, virtualInstrCountUnoptimized=1643, virtualRegCountOptimized=118, virtualRegCountUnoptimized=702),
|
||||
ExampleSizes("pointers/fountain-virtual", virtualInstrCountOptimized=146, virtualInstrCountUnoptimized=824, virtualRegCountOptimized=71, virtualRegCountUnoptimized=365),
|
||||
ExampleSizes("pointers/sorting", virtualInstrCountOptimized=369, virtualInstrCountUnoptimized=2336, virtualRegCountOptimized=173, virtualRegCountUnoptimized=1020)
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
@@ -354,11 +363,13 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
) { (params, prep) ->
|
||||
val filepath = prep.second
|
||||
val optimize = params.second
|
||||
val expectedLineCount = if (optimize) params.first.virtualLineCountOptimized else params.first.virtualLineCountUnoptimized
|
||||
val expectedInstrCount = if (optimize) params.first.virtualInstrCountOptimized else params.first.virtualInstrCountUnoptimized
|
||||
val expectedRegCount = if (optimize) params.first.virtualRegCountOptimized else params.first.virtualRegCountUnoptimized
|
||||
val result = compileTheThing(filepath, optimize, target, outputDir)
|
||||
result shouldNotBe null
|
||||
if (expectedLineCount > 0) {
|
||||
verifyOutputFileLineCount(result!!, expectedLineCount)
|
||||
if (expectedInstrCount > 0) {
|
||||
verifyIrInstructionCount(result!!, expectedInstrCount)
|
||||
verifyIrRegisterCount(result, expectedRegCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -203,7 +203,7 @@ class Return(val values: Array<Expression>, override val position: Position) : S
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = values.any{ it.referencesIdentifier(nameInSource) }
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun toString() = "Return($values, pos=$position)"
|
||||
override fun toString() = "Return(${values.contentToString()}, pos=$position)"
|
||||
}
|
||||
|
||||
class Break(override val position: Position) : Statement() {
|
||||
@@ -321,11 +321,11 @@ class VarDecl(
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
|
||||
}
|
||||
|
||||
fun createAuto(dt: DataType): VarDecl {
|
||||
fun createAuto(dt: DataType, position: Position): VarDecl {
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
val vardecl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, dt, ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
SplitWish.DONTCARE, null, autoVarName, emptyList(), null,
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = Position.DUMMY)
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = position)
|
||||
return vardecl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,23 @@ class IRProgram(val name: String,
|
||||
allAsmSubs().forEach { operation(it.asmChunk) }
|
||||
operation(globalInits)
|
||||
}
|
||||
|
||||
fun countCodeElements(): Pair<Int, Int> {
|
||||
var numInstr = 0
|
||||
var numChunks = 0
|
||||
foreachCodeChunk { chunk ->
|
||||
numChunks++
|
||||
numInstr += chunk.instructions.size
|
||||
}
|
||||
return Pair(numInstr, numChunks)
|
||||
}
|
||||
|
||||
fun countUsedRegisters(): Int {
|
||||
val used = registersUsed()
|
||||
return (used.readRegs.keys + used.writeRegs.keys).size +
|
||||
(used.readFpRegs.keys + used.writeFpRegs.keys).size
|
||||
}
|
||||
|
||||
fun getChunkWithLabel(label: String): IRCodeChunkBase {
|
||||
for(sub in allSubs()) {
|
||||
for(chunk in sub.chunks) {
|
||||
|
||||
@@ -15,5 +15,8 @@ interface ICodeGeneratorBackend {
|
||||
|
||||
interface IAssemblyProgram {
|
||||
val name: String
|
||||
val irInstructionCount: Int
|
||||
val irChunkCount: Int
|
||||
val irRegisterCount: Int
|
||||
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
|
||||
}
|
||||
Reference in new issue
Block a user