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

270 lines
14 KiB
Kotlin
Raw Normal View History

package prog8.codegen.intermediate
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")
return translateRegularAssign(assignment)
}
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
if(augmentedAssign.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
return if(ident!=null) {
assignVarAugmented(ident.name, assignment)
} else if(memory != null) {
if(memory.address is PtNumber)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
else
fallbackAssign(assignment)
} 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)
} else {
fallbackAssign(assignment)
}
}
private fun assignMemoryAugmented(
address: Int,
assignment: PtAugmentedAssign
): IRCodeChunks {
val value = assignment.value
val vmDt = codeGen.irType(value.type)
return when(assignment.operator) {
2023-03-13 20:29:57 +00:00
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value).chunks
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value).chunks
"*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value).chunks
"/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value).chunks
"|" -> expressionEval.operatorOrInplace(address, null, vmDt, value).chunks
"&" -> expressionEval.operatorAndInplace(address, null, vmDt, value).chunks
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value).chunks
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value).chunks
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value).chunks
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, 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
val valueVmDt = codeGen.irType(value.type)
return when (assignment.operator) {
2023-03-13 20:29:57 +00:00
"+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value).chunks
"-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value).chunks
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value).chunks
"/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value).chunks
"|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value).chunks
"&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value).chunks
"^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value).chunks
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value).chunks
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value).chunks
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
2022-09-14 23:32:48 +00:00
}
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
val normalAssign = PtAssignment(origAssign.position)
normalAssign.add(origAssign.target)
val value: PtExpression
if(origAssign.operator in PrefixOperators) {
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
value.add(origAssign.value)
2022-09-14 23:32:48 +00:00
} else {
require(origAssign.operator.endsWith('='))
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
}
normalAssign.add(value)
return translateRegularAssign(normalAssign)
}
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
val code= IRCodeChunk(null, null)
when(operator) {
"+" -> { }
"-" -> {
code += if(address!=null)
IRInstruction(Opcode.NEGM, vmDt, value = address)
2022-09-14 23:32:48 +00:00
else
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
}
"~" -> {
val regMask = codeGen.registers.nextFree()
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += if(address!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
2022-09-14 23:32:48 +00:00
else
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
}
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 ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type)
val result = mutableListOf<IRCodeChunkBase>()
var resultRegister = -1
var resultFpRegister = -1
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(assignment.value)
2023-03-13 02:48:35 +00:00
resultFpRegister = tr.resultFpReg
addToResult(result, tr, -1, tr.resultFpReg)
} else {
resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register
} else {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(assignment.value)
2023-03-13 02:48:35 +00:00
addToResult(result, tr, tr.resultReg, -1)
tr.resultReg
}
}
}
if(ident!=null) {
val instruction = if(zero) {
2022-12-30 17:07:53 +00:00
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name)
} else {
if (vmDt == IRDataType.FLOAT)
2022-12-30 17:07:53 +00:00
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name)
else
2022-12-30 17:07:53 +00:00
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = ident.name)
}
result += IRCodeChunk(null, null).also { it += instruction }
return result
}
else if(array!=null) {
2022-12-30 17:07:53 +00:00
val variable = array.variable.name
val itemsize = codeGen.program.memsizer.memorySize(array.type)
if(array.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)
throw AssemblyError("non-array var indexing requires bytes index")
2023-03-13 03:16:50 +00:00
val idxTr = expressionEval.translateExpression(array.index)
addToResult(result, idxTr, idxTr.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)
}
2023-03-13 03:16:50 +00:00
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxTr.resultReg, labelSymbol = variable)
result += code
return result
}
val fixedIndex = constIntValue(array.index)
if(zero) {
if(fixedIndex!=null) {
2022-09-14 23:32:48 +00:00
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
2022-09-14 23:32:48 +00:00
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, 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) }
}
} else {
if(fixedIndex!=null) {
2022-09-14 23:32:48 +00:00
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, 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) }
}
}
}
return result
}
else if(memory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.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()) }
result += chunk
} else {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(memory.address)
2023-03-13 03:16:50 +00:00
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=tr.resultReg) }
}
} 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()) }
result += chunk
} else {
2023-03-13 01:21:58 +00:00
val tr = expressionEval.translateExpression(memory.address)
2023-03-13 03:16:50 +00:00
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=tr.resultReg) }
}
}
return result
}
else
throw AssemblyError("weird assigntarget")
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
2023-03-13 01:21:58 +00:00
val result = mutableListOf<IRCodeChunkBase>()
2023-03-12 23:32:48 +00:00
val tr = if(itemsize==1) {
2023-03-13 01:21:58 +00:00
expressionEval.translateExpression(array.index)
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
2023-03-13 01:21:58 +00:00
expressionEval.translateExpression(mult)
}
2023-03-13 01:21:58 +00:00
addToResult(result, tr, indexReg, -1)
return result
}
}