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)
}
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
if(augmentedAssign.target.children.single() is PtMachineRegister)
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
if(augAssign.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return translateInplaceAssign(augmentedAssign)
}
private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks {
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val ident = augAssign.target.identifier
val memory = augAssign.target.memory
val array = augAssign.target.array
return if(ident!=null) {
assignVarAugmented(ident.name, assignment)
assignVarAugmented(ident.name, augAssign)
} else if(memory != null) {
if(memory.address is PtNumber)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
else
fallbackAssign(assignment)
fallbackAssign(augAssign)
} else if(array!=null) {
// 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
// will be optimized later and have the double assignments removed.
fallbackAssign(assignment)
fallbackAssign(augAssign)
} 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 {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type)
val result = mutableListOf<IRCodeChunkBase>()
var resultRegister = -1
var resultFpRegister = -1
var valueRegister = -1
var valueFpRegister = -1
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value)
resultFpRegister = tr.resultFpReg
addToResult(result, tr, -1, tr.resultFpReg)
valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister)
} else {
resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
} else {
val tr = expressionEval.translateExpression(assignment.value)
addToResult(result, tr, tr.resultReg, -1)
tr.resultReg
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
}
}
}
if(ident!=null) {
if(targetIdent!=null) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name)
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
} else {
if (vmDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name)
if (vmDt == IRDataType.FLOAT) {
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
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 }
return result
}
else if(array!=null) {
val variable = array.variable.name
val itemsize = codeGen.program.memsizer.memorySize(array.type)
else if(targetArray!=null) {
val variable = targetArray.variable.name
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
if(itemsize!=1)
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")
val idxTr = expressionEval.translateExpression(array.index)
addToResult(result, idxTr, idxTr.resultReg, -1)
val tr = expressionEval.translateExpression(targetArray.index)
val idxReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
val code = IRCodeChunk(null, null)
if(zero) {
// there's no STOREZIX instruction
resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
valueRegister = codeGen.registers.nextFree()
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
return result
}
val fixedIndex = constIntValue(array.index)
val fixedIndex = constIntValue(targetArray.index)
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
@ -198,53 +198,55 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += chunk
} else {
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) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
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
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) }
result += loadIndexReg(targetArray, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
}
} else {
if(fixedIndex!=null) {
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
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
result += loadIndexReg(targetArray, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
}
}
}
return result
}
else if(memory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.position}"}
else if(targetMemory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
if(zero) {
if(memory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(memory.address)
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
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 {
if(memory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, value=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(memory.address)
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
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
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name)
}
return result
} else {
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")
val rightConst = ifElse.condition.right.asConstInteger()
return if(rightConst==0)
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
else {
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
}
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
}

View File

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