From e7682119e0c866901db6086e982432b6401b9a21 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 2 Oct 2022 15:54:46 +0200 Subject: [PATCH] ir: count register uses --- docs/source/todo.rst | 2 - .../src/prog8/intermediate/IRFileWriter.kt | 35 ++++----- .../src/prog8/intermediate/IRInstructions.kt | 73 +++++++++---------- .../src/prog8/intermediate/IRProgram.kt | 59 ++++++--------- 4 files changed, 70 insertions(+), 99 deletions(-) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a63c39bd0..e3dd1f001 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- ir: RegistersUsed should actually count the number of times a register is used too? - ... diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index fe74a12f8..6c8ed3e81 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -33,28 +33,21 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { out.write("\n") out.close() - val usedRegisters = mutableSetOf() - val usedFpRegisters = mutableSetOf() - irProgram.blocks.forEach { - it.inlineAssembly.forEach { chunk -> - val used = chunk.usedRegisters() - usedRegisters += used.inputRegs + used.outputRegs - usedFpRegisters += used.inputFpRegs + used.outputFpRegs - } - it.subroutines.forEach { sub -> - sub.chunks.forEach { chunk -> - val used = chunk.usedRegisters() - usedRegisters += used.inputRegs + used.outputRegs - usedFpRegisters += used.inputFpRegs + used.outputFpRegs - } - } - it.asmSubroutines.forEach { asmsub -> - val used = asmsub.usedRegisters() - usedRegisters += used.inputRegs + used.outputRegs - usedFpRegisters += used.inputFpRegs + used.outputFpRegs - } + var usedRegisters = 0 + + fun addUsed(used: RegistersUsed) { + used.inputRegs.forEach{ (reg, count) -> usedRegisters+=count } + used.outputRegs.forEach{ (reg, count) -> usedRegisters+=count } + used.inputFpRegs.forEach{ (reg, count) -> usedRegisters+=count } + used.outputFpRegs.forEach{ (reg, count) -> usedRegisters+=count } } - println("($numLines lines in $numChunks code chunks, ${usedRegisters.size + usedFpRegisters.size} registers)") + + irProgram.blocks.forEach { + it.inlineAssembly.forEach { chunk -> addUsed(chunk.usedRegisters()) } + it.subroutines.flatMap { sub->sub.chunks }.forEach { chunk -> addUsed(chunk.usedRegisters()) } + it.asmSubroutines.forEach { asmsub -> addUsed(asmsub.usedRegisters()) } + } + println("($numLines lines in $numChunks code chunks, $usedRegisters registers)") return outfile } diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 05c781ece..540d3ba88 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -676,7 +676,6 @@ data class IRInstruction( val reg2direction: OperandDirection val fpReg1direction: OperandDirection val fpReg2direction: OperandDirection - private val registersUsed: RegistersUsed init { require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"} @@ -733,45 +732,43 @@ data class IRInstruction( else require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"} } - - val inputRegs = mutableSetOf() - val outputRegs = mutableSetOf() - val inputFpRegs = mutableSetOf() - val outputFpRegs = mutableSetOf() - - when (reg1direction) { - OperandDirection.UNUSED -> {} - OperandDirection.INPUT -> inputRegs += reg1!! - OperandDirection.OUTPUT -> outputRegs += reg1!! - OperandDirection.INOUT -> { - inputRegs += reg1!! - outputRegs += reg1!! - } - } - when (reg2direction) { - OperandDirection.UNUSED -> {} - OperandDirection.INPUT -> inputRegs += reg2!! - else -> throw IllegalArgumentException("reg2 can only be input") - } - when (fpReg1direction) { - OperandDirection.UNUSED -> {} - OperandDirection.INPUT -> inputFpRegs += fpReg1!! - OperandDirection.OUTPUT -> outputFpRegs += fpReg1!! - OperandDirection.INOUT -> { - inputFpRegs += fpReg1!! - outputFpRegs += fpReg1!! - } - } - when (fpReg2direction) { - OperandDirection.UNUSED -> {} - OperandDirection.INPUT -> inputFpRegs += fpReg2!! - else -> throw IllegalArgumentException("fpReg2 can only be input") - } - - registersUsed = RegistersUsed(inputRegs, outputRegs, inputFpRegs, outputFpRegs) } - override fun usedRegisters() = registersUsed + fun addUsedRegistersCounts( + inputRegs: MutableMap, + outputRegs: MutableMap, + inputFpRegs: MutableMap, + outputFpRegs: MutableMap + ) { + when (this.reg1direction) { + OperandDirection.UNUSED -> {} + OperandDirection.INPUT -> inputRegs[this.reg1!!] = inputRegs.getValue(this.reg1)+1 + OperandDirection.OUTPUT -> outputRegs[this.reg1!!] = outputRegs.getValue(this.reg1)+1 + OperandDirection.INOUT -> { + inputRegs[this.reg1!!] = inputRegs.getValue(this.reg1)+1 + outputRegs[this.reg1] = outputRegs.getValue(this.reg1)+1 + } + } + when (this.reg2direction) { + OperandDirection.UNUSED -> {} + OperandDirection.INPUT -> outputRegs[this.reg2!!] = outputRegs.getValue(this.reg2)+1 + else -> throw IllegalArgumentException("reg2 can only be input") + } + when (this.fpReg1direction) { + OperandDirection.UNUSED -> {} + OperandDirection.INPUT -> inputFpRegs[this.fpReg1!!] = inputFpRegs.getValue(this.fpReg1)+1 + OperandDirection.OUTPUT -> outputFpRegs[this.fpReg1!!] = outputFpRegs.getValue(this.fpReg1)+1 + OperandDirection.INOUT -> { + inputFpRegs[this.fpReg1!!] = inputFpRegs.getValue(this.fpReg1)+1 + outputFpRegs[this.fpReg1] = outputFpRegs.getValue(this.fpReg1)+1 + } + } + when (this.fpReg2direction) { + OperandDirection.UNUSED -> {} + OperandDirection.INPUT -> inputFpRegs[this.fpReg2!!] = inputFpRegs.getValue(this.fpReg2)+1 + else -> throw IllegalArgumentException("fpReg2 can only be input") + } + } override fun toString(): String { val result = mutableListOf(opcode.name.lowercase()) diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index a8079c2cf..36cbfaea9 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -144,21 +144,13 @@ class IRAsmSubroutine( fun usedRegisters() = registersUsed } -sealed class IRCodeLine { - abstract fun usedRegisters(): RegistersUsed -} +sealed class IRCodeLine -class IRCodeLabel(val name: String): IRCodeLine() { - override fun usedRegisters() = RegistersUsed.EMPTY -} +class IRCodeLabel(val name: String): IRCodeLine() -class IRCodeComment(val comment: String): IRCodeLine() { - override fun usedRegisters() = RegistersUsed.EMPTY -} +class IRCodeComment(val comment: String): IRCodeLine() -class IRCodeInlineBinary(val data: Collection): IRCodeLine() { - override fun usedRegisters() = RegistersUsed.EMPTY -} +class IRCodeInlineBinary(val data: Collection): IRCodeLine() abstract class IRCodeChunkBase(val position: Position) { val lines = mutableListOf() @@ -173,16 +165,13 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) { override fun isEmpty() = lines.isEmpty() override fun isNotEmpty() = lines.isNotEmpty() override fun usedRegisters(): RegistersUsed { - val inputRegs = mutableSetOf() - val outputRegs = mutableSetOf() - val inputFpRegs = mutableSetOf() - val outputFpRegs = mutableSetOf() + val inputRegs = mutableMapOf().withDefault { 0 } + val inputFpRegs = mutableMapOf().withDefault { 0 } + val outputRegs = mutableMapOf().withDefault { 0 } + val outputFpRegs = mutableMapOf().withDefault { 0 } lines.forEach { - val used = it.usedRegisters() - inputRegs += used.inputRegs - outputRegs += used.outputRegs - inputFpRegs += used.inputFpRegs - outputFpRegs += used.outputFpRegs + if(it is IRInstruction) + it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) } return RegistersUsed(inputRegs, outputRegs, inputFpRegs, outputFpRegs) } @@ -197,10 +186,11 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) { } class RegistersUsed( - val inputRegs: Set, - val outputRegs: Set, - val inputFpRegs: Set, - val outputFpRegs: Set + // register num -> number of uses + val inputRegs: Map, + val outputRegs: Map, + val inputFpRegs: Map, + val outputFpRegs: Map, ) { override fun toString(): String { return "input=$inputRegs, output=$outputRegs, inputFp=$inputFpRegs, outputFp=$outputFpRegs" @@ -208,10 +198,6 @@ class RegistersUsed( fun isEmpty() = inputRegs.isEmpty() && outputRegs.isEmpty() && inputFpRegs.isEmpty() && outputFpRegs.isEmpty() fun isNotEmpty() = !isEmpty() - - companion object { - val EMPTY = RegistersUsed(emptySet(), emptySet(), emptySet(), emptySet()) - } } class IRInlineAsmChunk(val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(position) { @@ -229,19 +215,16 @@ class IRInlineAsmChunk(val assembly: String, val isIR: Boolean, position: Positi } private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersUsed { - val inputRegs = mutableSetOf() - val inputFpRegs = mutableSetOf() - val outputRegs = mutableSetOf() - val outputFpRegs = mutableSetOf() + val inputRegs = mutableMapOf().withDefault { 0 } + val inputFpRegs = mutableMapOf().withDefault { 0 } + val outputRegs = mutableMapOf().withDefault { 0 } + val outputFpRegs = mutableMapOf().withDefault { 0 } if(isIR) { assembly.lineSequence().forEach { line -> val code = parseIRCodeLine(line.trim(), 0, mutableMapOf()) - val used = code.usedRegisters() - inputRegs += used.inputRegs - outputRegs += used.outputRegs - inputFpRegs += used.inputFpRegs - outputFpRegs += used.outputFpRegs + if(code is IRInstruction) + code.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) } }