ir: remove a bunch of strange in-place assignment operators, and problematic opcodes

This commit is contained in:
Irmen de Jong
2024-12-27 18:22:15 +01:00
parent 942d3ee640
commit e2882d37bf
6 changed files with 87 additions and 392 deletions
@@ -98,12 +98,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
"==" -> operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
"!=" -> operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<" -> operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
">" -> operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"<=" -> operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
">=" -> operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
@@ -1140,293 +1134,4 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
return result
}
private fun operatorEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, "==")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SEQ)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SEQ)
}
return chunks
}
private fun operatorNotEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, "!=")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SNE)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SNE)
}
return chunks
}
private fun operatorGreaterInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, ">")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val opcode = if(signed) Opcode.SGTS else Opcode.SGT
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
}
return chunks
}
private fun operatorLessInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, "<")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val opcode = if(signed) Opcode.SLTS else Opcode.SLT
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
}
return chunks
}
private fun operatorGreaterEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, ">=")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val opcode = if(signed) Opcode.SGES else Opcode.SGE
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
}
return chunks
}
private fun operatorLessEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
if(array!=null)
return createInplaceArrayComparison(array, operand, "<=")
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place compare"")
val opcode = if(signed) Opcode.SLES else Opcode.SLE
val chunks = if(vmDt==IRDataType.FLOAT) {
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
} else {
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
}
return chunks
}
private fun createInplaceComparison(
constAddress: Int?,
symbol: String?,
vmDt: IRDataType,
operand: PtExpression,
compareAndSetOpcode: Opcode
): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
val valueReg = codeGen.registers.nextFree()
val cmpResultReg = codeGen.registers.nextFree()
if(operand is PtNumber || operand is PtBool) {
if(operand is PtNumber && operand.number==0.0 && compareAndSetOpcode in arrayOf(Opcode.SEQ, Opcode.SNE)) {
// ==0 or !=0 optimized case
val compareAndSetOpcodeZero = if(compareAndSetOpcode==Opcode.SEQ) Opcode.SZ else Opcode.SNZ
if (constAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1 = cmpResultReg, reg2 = valueReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1=cmpResultReg, reg2 = valueReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
return result
}
// compare against number that is not 0
val numberReg = codeGen.registers.nextFree()
val value = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
if (constAddress != null) {
// in-place modify a memory location
val innervalue = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = innervalue)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = value)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
} else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
if (constAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
}
return result
}
private fun createInplaceFloatComparison(
constAddress: Int?,
symbol: String?,
operand: PtExpression,
compareAndSetOpcode: Opcode
): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
val valueReg = codeGen.registers.nextFreeFloat()
val cmpReg = codeGen.registers.nextFree()
val zeroReg = codeGen.registers.nextFree()
if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFreeFloat()
val cmpResultReg = codeGen.registers.nextFree()
if (constAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
}
}
} else {
val tr = expressionEval.translateExpression(operand)
val cmpResultReg = codeGen.registers.nextFree()
addToResult(result, tr, -1, tr.resultFpReg)
if (constAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
}
}
}
return result
}
private fun createInplaceArrayComparison(array: PtArrayIndexer, value: PtExpression, comparisonOperator: String): IRCodeChunks? {
if(array.type.isFloat)
return null // TODO("optimized in-place compare on float arrays"))
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
TODO("inplace compare for split word array")
val vmDt = irType(array.type)
val constIndex = array.index.asConstInteger()
val constValue = value.asConstInteger()
val cmpResultReg = codeGen.registers.nextFree()
if(constIndex!=null) {
if(constValue==0) {
// comparison against zero.
val valueReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += when(comparisonOperator) {
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"<" -> return null // TODO("array <0 inplace"))
"<=" -> return null // TODO("array <=0 inplace"))
">" -> return null // TODO("array >0 inplace"))
">=" -> return null // TODO("array >=0 inplace"))
else -> throw AssemblyError("invalid operator")
}
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
}
return result
} else {
return null // TODO("compare against non-zero value"))
}
} else {
if(constValue==0) {
// comparison against zero.
val valueReg = codeGen.registers.nextFree()
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
it += when(comparisonOperator) {
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"<" -> return null // TODO("array <0 inplace"))
"<=" -> return null // TODO("array <=0 inplace"))
">" -> return null // TODO("array >0 inplace"))
">=" -> return null // TODO("array >=0 inplace"))
else -> throw AssemblyError("invalid operator")
}
it += IRInstruction(Opcode.STOREX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
}
return result
}
else
return null // TODO("compare against non-zero value"))
}
}
}
@@ -429,10 +429,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.BOOL -> {
when {
valueDt.isByte -> {
// loadStatusAsBooleanResult(Opcode.BSTNE, result)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
valueDt.isWord -> {
// loadStatusAsBooleanResult(Opcode.BSTNE, result)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
@@ -720,7 +722,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
finalReturnRegister = codeGen.registers.nextFree()
when(statusFlagResult) {
Statusflag.Pc -> {
addInstr(result, IRInstruction(Opcode.SCS, returnRegSpec.dt, reg1=finalReturnRegister), null)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, returnRegSpec.dt, reg1 = finalReturnRegister, immediate = 0)
it += IRInstruction(Opcode.ROXL, returnRegSpec.dt, reg1 = finalReturnRegister)
}
}
else -> {
val branchOpcode = when(statusFlagResult) {
@@ -956,14 +961,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val valueReg = codeGen.registers.nextFree()
val label = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=valueReg, immediate = 0)
it += if (notEquals)
IRInstruction(Opcode.BSTNE, labelSymbol = label)
else
IRInstruction(Opcode.BSTEQ, labelSymbol = label)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0)
else
IRInstruction(Opcode.BSTNE, labelSymbol = label)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=resultRegister)
}
result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
@@ -971,27 +976,41 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
return if(binExpr.right.asConstValue()==0.0) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNZ else Opcode.SZ
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = tr.resultReg), null)
val rightConst = binExpr.right.asConstValue()
return if(rightConst!=null) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CMPI, leftTr.dt, reg1 = leftTr.resultReg, immediate = rightConst.toInt()), null)
val resultReg = loadStatusAsBooleanResult(if(notEquals) Opcode.BSTNE else Opcode.BSTEQ, result)
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
addInstr(result, IRInstruction(Opcode.CMP, leftTr.dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg), null)
val resultReg = loadStatusAsBooleanResult(if(notEquals) Opcode.BSTNE else Opcode.BSTEQ, result)
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
}
}
}
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
val other = codeGen.createLabelName()
val after = codeGen.createLabelName()
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(branchForTrue, labelSymbol = other)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
result += IRCodeChunk(after, null)
return resultReg
}
private fun operatorShiftRight(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(codeGen.isOne(binExpr.right)) {