prog8/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt

432 lines
24 KiB
Kotlin
Raw Normal View History

package prog8.codegen.intermediate
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PrefixOperators
import prog8.code.core.SignedDatatypes
import prog8.intermediate.*
2022-09-19 17:41:43 +00:00
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): IRCodeChunks {
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
val chunks = translateRegularAssign(assignment)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(assignment.position)
return chunks
}
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")
val ident = augAssign.target.identifier
val memory = augAssign.target.memory
val array = augAssign.target.array
val chunks = if(ident!=null) {
assignVarAugmented(ident.name, augAssign)
} else if(memory != null) {
if(memory.address is PtNumber)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
else
fallbackAssign(augAssign)
} else if(array!=null) {
assignArrayAugmented(array, augAssign)
} else {
fallbackAssign(augAssign)
}
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks
}
private fun assignMemoryAugmented(address: Int, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value
2024-01-16 19:41:53 +00:00
val targetDt = irType(assignment.target.type)
val signed = assignment.target.type in SignedDatatypes
return when(assignment.operator) {
2024-01-16 19:41:53 +00:00
"+=" -> expressionEval.operatorPlusInplace(address, null, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(address, null, targetDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(address, null, targetDt, value)
"/=" -> expressionEval.operatorDivideInplace(address, null, targetDt, signed, value)
"|=" -> expressionEval.operatorOrInplace(address, null, targetDt, value)
"&=" -> expressionEval.operatorAndInplace(address, null, targetDt, value)
"^=" -> expressionEval.operatorXorInplace(address, null, targetDt, value)
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, targetDt, value)
">>=" -> expressionEval.operatorShiftRightInplace(address, null, targetDt, signed, value)
"%=" -> expressionEval.operatorModuloInplace(address, null, targetDt, value)
"==" -> expressionEval.operatorEqualsNotEqualsInplace(address, null, targetDt, value)
2024-01-16 19:41:53 +00:00
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, targetDt, value)
"<" -> expressionEval.operatorLessInplace(address, null, targetDt, signed, value)
">" -> expressionEval.operatorGreaterInplace(address, null, targetDt, signed, value)
"<=" -> expressionEval.operatorLessEqualInplace(address, null, targetDt, signed, value)
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, targetDt, signed, value)
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, address, null)
2023-03-13 20:29:57 +00:00
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
}
2022-09-14 23:32:48 +00:00
}
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value
2024-01-16 19:41:53 +00:00
val signed = assignment.target.type in SignedDatatypes
2023-04-29 12:22:04 +00:00
val targetDt = irType(assignment.target.type)
2024-01-16 19:41:53 +00:00
return when(assignment.operator) {
2023-04-05 18:48:20 +00:00
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
2024-01-16 19:41:53 +00:00
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, signed, value)
2023-04-05 18:48:20 +00:00
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
"or=" -> expressionEval.operatorLogicalOrInplace(null, symbol, targetDt, value)
2023-04-05 18:48:20 +00:00
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
"and=" -> expressionEval.operatorLogicalAndInplace(null, symbol, targetDt, value)
"^=", "xor=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
2023-04-05 18:48:20 +00:00
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
2024-01-16 19:41:53 +00:00
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, signed, value)
2023-04-05 18:48:20 +00:00
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
"==" -> expressionEval.operatorEqualsNotEqualsInplace(null, symbol, targetDt, value)
2023-04-05 18:48:20 +00:00
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
2024-01-16 19:41:53 +00:00
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, signed, value)
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, signed, value)
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, signed, value)
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, signed, value)
2023-04-05 18:48:20 +00:00
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
}
}
private fun assignArrayAugmented(array: PtArrayIndexer, assignment: PtAugmentedAssign): IRCodeChunks {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val value = assignment.value
val signed = assignment.target.type in SignedDatatypes
val result = when(assignment.operator) {
// TODO implement all of these:
// "+=" -> expressionEval.operatorPlusInplace(array, eltSize, value)
// "-=" -> expressionEval.operatorMinusInplace(array, eltSize, value)
// "*=" -> expressionEval.operatorMultiplyInplace(array, eltSize, value)
// "/=" -> expressionEval.operatorDivideInplace(array, eltSize, signed, value)
// "|=" -> expressionEval.operatorOrInplace(array, eltSize, value)
// "&=" -> expressionEval.operatorAndInplace(array, eltSize, value)
// "^=" -> expressionEval.operatorXorInplace(array, eltSize, value)
// "<<=" -> expressionEval.operatorShiftLeftInplace(array, eltSize, value)
// ">>=" -> expressionEval.operatorShiftRightInplace(array, eltSize, signed, value)
// "%=" -> expressionEval.operatorModuloInplace(array, eltSize, value)
"==" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, true)
"!=" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, false)
// "<" -> expressionEval.operatorLessInplace(array, eltSize, signed, value) // TODO reuse code from ==
// ">" -> expressionEval.operatorGreaterInplace(array, eltSize, signed, value) // TODO reuse code from ==
// "<=" -> expressionEval.operatorLessEqualInplace(array, eltSize, signed, value) // TODO reuse code from ==
// ">=" -> expressionEval.operatorGreaterEqualInplace(array, eltSize, signed, value) // TODO reuse code from ==
in PrefixOperators -> inplacePrefix(assignment.operator, array, eltSize)
// else -> Err(NotImplementedError("invalid augmented assign operator ${assignment.operator}"))
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
2022-09-14 23:32:48 +00:00
}
return result.getOrElse { fallbackAssign(assignment) }
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
val value: PtExpression
if(origAssign.operator in PrefixOperators) {
2024-01-16 19:41:53 +00:00
value = PtPrefix(origAssign.operator, origAssign.target.type, origAssign.value.position)
value.add(origAssign.value)
2022-09-14 23:32:48 +00:00
} else {
require(origAssign.operator.endsWith('='))
val operator = if(origAssign.operator=="==") "==" else origAssign.operator.dropLast(1)
value = PtBinaryExpression(operator, origAssign.target.type, origAssign.value.position)
2023-07-02 13:26:04 +00:00
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
}
val normalAssign = PtAssignment(origAssign.position)
normalAssign.add(origAssign.target)
normalAssign.add(value)
return translateRegularAssign(normalAssign)
}
private fun inplacePrefix(operator: String, array: PtArrayIndexer, eltSize: Int): Result<IRCodeChunks, NotImplementedError> {
if(array.splitWords)
TODO("inplace prefix for split word array")
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(array.type)
val constIndex = array.index.asConstInteger()
when(operator) {
"+" -> { }
"-" -> {
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
} else {
val register = codeGen.registers.nextFree()
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
if(eltSize>1)
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
}
}
}
"~" -> {
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
} else {
val register = codeGen.registers.nextFree()
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
if(eltSize>1)
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
}
}
}
"not" -> {
val register = codeGen.registers.nextFree()
if(constIndex!=null) {
// TODO: in boolean branch, is 'not' handled ok like this?
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
}
} else {
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
if(eltSize>1)
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name)
}
}
}
else -> throw AssemblyError("weird prefix operator")
}
return Ok(result)
}
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
val code= IRCodeChunk(null, null)
when(operator) {
"+" -> { }
"-" -> code += IRInstruction(Opcode.NEGM, vmDt, address = address, labelSymbol = symbol)
"~" -> code += IRInstruction(Opcode.INVM, vmDt, address = address, labelSymbol = symbol)
// TODO: in boolean branch, how is 'not' handled here?
else -> throw AssemblyError("weird prefix operator")
}
return listOf(code)
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
var valueRegister = -1
var valueFpRegister = -1
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (valueDt == IRDataType.FLOAT) {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(assignment.value)
valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister)
} else {
val extendByteToWord = if(targetDt != valueDt) {
// usually an error EXCEPT when a byte is assigned to a word.
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
true
else
throw AssemblyError("assignment value and target dt mismatch")
} else false
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtMachineRegister).register), null)
}
} else {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
}
}
}
}
if(targetIdent!=null) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
} else {
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
else
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
}
result += IRCodeChunk(null, null).also { it += instruction }
return result
}
else if(targetArray!=null) {
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
if(targetArray.usesPointerVariable) {
if(itemsize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(targetArray.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
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
valueRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
}
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code
return result
}
val fixedIndex = constIntValue(targetArray.index)
2023-07-07 13:43:09 +00:00
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) {
if(fixedIndex!=null) {
2023-05-26 20:56:12 +00:00
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
2023-05-26 20:56:12 +00:00
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
2023-05-26 20:56:12 +00:00
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
2023-05-26 20:56:12 +00:00
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
2023-05-26 20:56:12 +00:00
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
2023-05-26 20:56:12 +00:00
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
2022-09-14 23:32:48 +00:00
val offset = fixedIndex*itemsize
2023-05-26 20:56:12 +00:00
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
2023-05-26 20:56:12 +00:00
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
2023-05-26 20:56:12 +00:00
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
2023-05-26 20:56:12 +00:00
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
2023-05-26 20:56:12 +00:00
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
2023-05-26 20:56:12 +00:00
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
2023-05-26 20:56:12 +00:00
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
2023-05-26 20:56:12 +00:00
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
2023-05-26 20:56:12 +00:00
}
}
}
}
return result
}
else if(targetMemory!=null) {
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
if(zero) {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
2023-03-13 03:16:50 +00:00
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
}
} else {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
2023-03-13 03:16:50 +00:00
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
}
}
return result
}
else
throw AssemblyError("weird assigntarget")
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
2023-05-26 20:56:12 +00:00
// returns the code to load the Index into the register, which is also returned.
2023-04-03 01:13:35 +00:00
val result = mutableListOf<IRCodeChunkBase>()
2023-05-26 20:56:12 +00:00
if(itemsize==1 || array.splitWords) {
2023-04-03 01:13:35 +00:00
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
2023-07-02 13:26:04 +00:00
val mult: PtExpression
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
}