IR: more optimal branch instructions for comparisons against zero

This commit is contained in:
Irmen de Jong 2023-03-13 21:53:02 +01:00
parent 025183602f
commit dc316fd7b4
3 changed files with 152 additions and 107 deletions

View File

@ -16,32 +16,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return translateRegularAssign(assignment) return translateRegularAssign(assignment)
} }
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks { internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
if(augmentedAssign.target.children.single() is PtMachineRegister) if(augAssign.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return translateInplaceAssign(augmentedAssign) val ident = augAssign.target.identifier
} val memory = augAssign.target.memory
val array = augAssign.target.array
private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks {
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
return if(ident!=null) { return if(ident!=null) {
assignVarAugmented(ident.name, assignment) assignVarAugmented(ident.name, augAssign)
} else if(memory != null) { } else if(memory != null) {
if(memory.address is PtNumber) if(memory.address is PtNumber)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment) assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
else else
fallbackAssign(assignment) fallbackAssign(augAssign)
} else if(array!=null) { } else if(array!=null) {
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times // NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly // in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
// will be optimized later and have the double assignments removed. // will be optimized later and have the double assignments removed.
fallbackAssign(assignment) fallbackAssign(augAssign)
} else { } else {
fallbackAssign(assignment) fallbackAssign(augAssign)
} }
} }
@ -131,66 +127,70 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks { private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call. // note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier val targetIdent = assignment.target.identifier
val memory = assignment.target.memory val targetMemory = assignment.target.memory
val array = assignment.target.array val targetArray = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type) val vmDt = codeGen.irType(assignment.value.type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
var resultRegister = -1
var resultFpRegister = -1 var valueRegister = -1
var valueFpRegister = -1
val zero = codeGen.isZero(assignment.value) val zero = codeGen.isZero(assignment.value)
if(!zero) { if(!zero) {
// calculate the assignment value // calculate the assignment value
if (vmDt == IRDataType.FLOAT) { if (vmDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
resultFpRegister = tr.resultFpReg valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, valueFpRegister)
} else { } else {
resultRegister = if (assignment.value is PtMachineRegister) { if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register valueRegister = (assignment.value as PtMachineRegister).register
} else { } else {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
addToResult(result, tr, tr.resultReg, -1) valueRegister = tr.resultReg
tr.resultReg addToResult(result, tr, valueRegister, -1)
} }
} }
} }
if(ident!=null) {
if(targetIdent!=null) {
val instruction = if(zero) { val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name) IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
} else { } else {
if (vmDt == IRDataType.FLOAT) if (vmDt == IRDataType.FLOAT) {
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name) IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
else else
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = ident.name) IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
} }
result += IRCodeChunk(null, null).also { it += instruction } result += IRCodeChunk(null, null).also { it += instruction }
return result return result
} }
else if(array!=null) { else if(targetArray!=null) {
val variable = array.variable.name val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(array.type) val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
if(array.variable.type==DataType.UWORD) { if(targetArray.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string // indexing a pointer var instead of a real array or string
if(itemsize!=1) if(itemsize!=1)
throw AssemblyError("non-array var indexing requires bytes dt") throw AssemblyError("non-array var indexing requires bytes dt")
if(array.index.type!=DataType.UBYTE) if(targetArray.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index") throw AssemblyError("non-array var indexing requires bytes index")
val idxTr = expressionEval.translateExpression(array.index) val tr = expressionEval.translateExpression(targetArray.index)
addToResult(result, idxTr, idxTr.resultReg, -1) val idxReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(zero) { if(zero) {
// there's no STOREZIX instruction // there's no STOREZIX instruction
resultRegister = codeGen.registers.nextFree() valueRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0) code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, value=0)
} }
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxTr.resultReg, labelSymbol = variable) code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code result += code
return result return result
} }
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(targetArray.index)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
@ -198,53 +198,55 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += chunk result += chunk
} else { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg) result += loadIndexReg(targetArray, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) } result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
} }
} else { } else {
if(vmDt== IRDataType.FLOAT) { if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") } val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
result += chunk result += chunk
} else { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg) result += loadIndexReg(targetArray, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) } result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") } val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
result += chunk result += chunk
} else { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg) result += loadIndexReg(targetArray, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) } result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
} }
} }
} }
return result return result
} }
else if(memory!=null) { else if(targetMemory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.position}"} require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
if(zero) { if(zero) {
if(memory.address is PtNumber) { if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) } val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk result += chunk
} else { } else {
val tr = expressionEval.translateExpression(memory.address) val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=tr.resultReg) } result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
} }
} else { } else {
if(memory.address is PtNumber) { if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) } val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, value=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk result += chunk
} else { } else {
val tr = expressionEval.translateExpression(memory.address) val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=tr.resultReg) } result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
} }
} }

View File

@ -937,57 +937,100 @@ class IRCodeGen(
else else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name) IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name)
} }
return result
} else { } else {
val leftTr = expressionEval.translateExpression(ifElse.condition.left) val rightConst = ifElse.condition.right.asConstInteger()
addToResult(result, leftTr, leftTr.resultReg, -1) return if(rightConst==0)
val rightTr = expressionEval.translateExpression(ifElse.condition.right) ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
require(rightTr.resultReg!=leftTr.resultReg) else {
addToResult(result, rightTr, rightTr.resultReg, -1) ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
val opcode: Opcode
val firstReg: Int
val secondReg: Int
when (ifElse.condition.operator) {
"==" -> {
opcode = Opcode.BEQ
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"!=" -> {
opcode = Opcode.BNE
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"<" -> {
// swapped '>'
opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = rightTr.resultReg
secondReg = leftTr.resultReg
}
">" -> {
opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"<=" -> {
// swapped '>='
opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = rightTr.resultReg
secondReg = leftTr.resultReg
}
">=" -> {
opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
else -> throw AssemblyError("invalid comparison operator")
} }
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
} }
}
private fun ifZeroIntThenJump(
result: MutableList<IRCodeChunkBase>,
ifElse: PtIfElse,
signed: Boolean,
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val opcode = when (ifElse.condition.operator) {
"==" -> Opcode.BZ
"!=" -> Opcode.BNZ
"<" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) Opcode.BGZS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) Opcode.BLEZS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, value = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.identifier!!.name), null)
return result
}
private fun ifNonZeroIntThenJump(
result: MutableList<IRCodeChunkBase>,
ifElse: PtIfElse,
signed: Boolean,
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = expressionEval.translateExpression(ifElse.condition.right)
require(rightTr.resultReg!=leftTr.resultReg)
addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode: Opcode
val firstReg: Int
val secondReg: Int
when (ifElse.condition.operator) {
"==" -> {
opcode = Opcode.BEQ
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"!=" -> {
opcode = Opcode.BNE
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"<" -> {
// swapped '>'
opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = rightTr.resultReg
secondReg = leftTr.resultReg
}
">" -> {
opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
"<=" -> {
// swapped '>='
opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = rightTr.resultReg
secondReg = leftTr.resultReg
}
">=" -> {
opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = leftTr.resultReg
secondReg = rightTr.resultReg
}
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
return result return result
} }

View File

@ -386,7 +386,7 @@ main {
val irProgram = IRFileReader().read(virtfile) val irProgram = IRFileReader().read(virtfile)
val start = irProgram.blocks[0].children[0] as IRSubroutine val start = irProgram.blocks[0].children[0] as IRSubroutine
val instructions = start.chunks.flatMap { c->c.instructions } val instructions = start.chunks.flatMap { c->c.instructions }
instructions.size shouldBe 18 instructions.size shouldBe 13
instructions.last().opcode shouldBe Opcode.RETURN instructions.last().opcode shouldBe Opcode.RETURN
} }