2022-09-19 16:09:56 +00:00
|
|
|
package prog8.codegen.intermediate
|
2022-08-21 15:21:29 +00:00
|
|
|
|
|
|
|
import prog8.code.ast.*
|
2022-08-27 11:06:44 +00:00
|
|
|
import prog8.code.core.AssemblyError
|
|
|
|
import prog8.code.core.DataType
|
2023-02-15 21:50:35 +00:00
|
|
|
import prog8.code.core.PrefixOperators
|
2022-08-27 11:06:44 +00:00
|
|
|
import prog8.code.core.SignedDatatypes
|
2022-10-06 22:34:56 +00:00
|
|
|
import prog8.intermediate.*
|
2022-08-21 15:21:29 +00:00
|
|
|
|
2022-09-19 17:41:43 +00:00
|
|
|
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
|
2022-08-21 15:21:29 +00:00
|
|
|
|
2022-10-06 22:34:56 +00:00
|
|
|
internal fun translate(assignment: PtAssignment): IRCodeChunks {
|
2022-08-21 15:21:29 +00:00
|
|
|
if(assignment.target.children.single() is PtMachineRegister)
|
|
|
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
|
|
|
|
2023-02-15 21:50:35 +00:00
|
|
|
return translateRegularAssign(assignment)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2023-03-13 20:53:02 +00:00
|
|
|
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
|
|
|
if(augAssign.target.children.single() is PtMachineRegister)
|
2023-02-15 21:50:35 +00:00
|
|
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
|
|
|
|
2023-03-13 20:53:02 +00:00
|
|
|
val ident = augAssign.target.identifier
|
|
|
|
val memory = augAssign.target.memory
|
|
|
|
val array = augAssign.target.array
|
2022-08-21 15:21:29 +00:00
|
|
|
|
|
|
|
return if(ident!=null) {
|
2023-03-13 20:53:02 +00:00
|
|
|
assignVarAugmented(ident.name, augAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else if(memory != null) {
|
|
|
|
if(memory.address is PtNumber)
|
2023-03-13 20:53:02 +00:00
|
|
|
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
else
|
2023-03-13 20:53:02 +00:00
|
|
|
fallbackAssign(augAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
} 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.
|
2023-03-13 20:53:02 +00:00
|
|
|
fallbackAssign(augAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
fallbackAssign(augAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-15 21:50:35 +00:00
|
|
|
private fun assignMemoryAugmented(
|
2022-08-21 15:21:29 +00:00
|
|
|
address: Int,
|
2023-02-15 21:50:35 +00:00
|
|
|
assignment: PtAugmentedAssign
|
2022-10-06 22:34:56 +00:00
|
|
|
): IRCodeChunks {
|
2023-02-15 21:50:35 +00:00
|
|
|
val value = assignment.value
|
2022-09-30 13:27:03 +00:00
|
|
|
val vmDt = codeGen.irType(value.type)
|
2023-02-15 21:50:35 +00:00
|
|
|
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
|
2023-02-15 21:50:35 +00:00
|
|
|
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
2023-03-13 20:29:57 +00:00
|
|
|
|
2023-02-16 21:45:35 +00:00
|
|
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2022-09-14 23:32:48 +00:00
|
|
|
}
|
2022-08-21 15:21:29 +00:00
|
|
|
|
2023-02-15 21:50:35 +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
|
2023-02-15 21:50:35 +00:00
|
|
|
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
|
2023-02-16 21:45:35 +00:00
|
|
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
2022-09-14 23:32:48 +00:00
|
|
|
}
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2023-02-15 21:50:35 +00:00
|
|
|
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
2022-08-21 15:21:29 +00:00
|
|
|
if (codeGen.options.slowCodegenWarnings)
|
|
|
|
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
2023-02-15 21:50:35 +00:00
|
|
|
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 {
|
2023-02-15 21:50:35 +00:00
|
|
|
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)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-02-15 21:50:35 +00:00
|
|
|
normalAssign.add(value)
|
|
|
|
return translateRegularAssign(normalAssign)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2023-02-15 21:50:35 +00:00
|
|
|
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
|
2022-11-02 21:12:42 +00:00
|
|
|
val code= IRCodeChunk(null, null)
|
2022-08-21 15:21:29 +00:00
|
|
|
when(operator) {
|
|
|
|
"+" -> { }
|
|
|
|
"-" -> {
|
2023-02-15 21:50:35 +00:00
|
|
|
code += if(address!=null)
|
|
|
|
IRInstruction(Opcode.NEGM, vmDt, value = address)
|
2022-09-14 23:32:48 +00:00
|
|
|
else
|
2023-02-15 21:50:35 +00:00
|
|
|
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
"~" -> {
|
2022-09-30 13:27:03 +00:00
|
|
|
val regMask = codeGen.registers.nextFree()
|
|
|
|
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
2022-09-26 17:46:44 +00:00
|
|
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
2023-02-15 21:50:35 +00:00
|
|
|
code += if(address!=null)
|
|
|
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
2022-09-14 23:32:48 +00:00
|
|
|
else
|
2023-02-15 21:50:35 +00:00
|
|
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
else -> throw AssemblyError("weird prefix operator")
|
|
|
|
}
|
2022-10-06 22:34:56 +00:00
|
|
|
return listOf(code)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2022-10-06 22:34:56 +00:00
|
|
|
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
2022-08-21 15:21:29 +00:00
|
|
|
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
2023-03-13 20:53:02 +00:00
|
|
|
val targetIdent = assignment.target.identifier
|
|
|
|
val targetMemory = assignment.target.memory
|
|
|
|
val targetArray = assignment.target.array
|
2022-09-30 13:27:03 +00:00
|
|
|
val vmDt = codeGen.irType(assignment.value.type)
|
2022-10-06 22:34:56 +00:00
|
|
|
val result = mutableListOf<IRCodeChunkBase>()
|
2023-03-13 20:53:02 +00:00
|
|
|
|
|
|
|
var valueRegister = -1
|
|
|
|
var valueFpRegister = -1
|
2022-08-21 15:21:29 +00:00
|
|
|
val zero = codeGen.isZero(assignment.value)
|
|
|
|
if(!zero) {
|
|
|
|
// calculate the assignment value
|
2022-09-30 13:27:03 +00:00
|
|
|
if (vmDt == IRDataType.FLOAT) {
|
2023-03-13 01:21:58 +00:00
|
|
|
val tr = expressionEval.translateExpression(assignment.value)
|
2023-03-13 20:53:02 +00:00
|
|
|
valueFpRegister = tr.resultFpReg
|
|
|
|
addToResult(result, tr, -1, valueFpRegister)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
if (assignment.value is PtMachineRegister) {
|
|
|
|
valueRegister = (assignment.value as PtMachineRegister).register
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 01:21:58 +00:00
|
|
|
val tr = expressionEval.translateExpression(assignment.value)
|
2023-03-13 20:53:02 +00:00
|
|
|
valueRegister = tr.resultReg
|
|
|
|
addToResult(result, tr, valueRegister, -1)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-13 20:53:02 +00:00
|
|
|
|
|
|
|
if(targetIdent!=null) {
|
2022-10-06 22:34:56 +00:00
|
|
|
val instruction = if(zero) {
|
2023-03-13 20:53:02 +00:00
|
|
|
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
if (vmDt == IRDataType.FLOAT) {
|
|
|
|
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
|
|
|
}
|
2022-08-21 15:21:29 +00:00
|
|
|
else
|
2023-03-13 20:53:02 +00:00
|
|
|
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2022-11-02 21:12:42 +00:00
|
|
|
result += IRCodeChunk(null, null).also { it += instruction }
|
2022-10-06 22:34:56 +00:00
|
|
|
return result
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-03-13 20:53:02 +00:00
|
|
|
else if(targetArray!=null) {
|
|
|
|
val variable = targetArray.variable.name
|
|
|
|
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
2022-08-21 15:21:29 +00:00
|
|
|
|
2023-03-13 20:53:02 +00:00
|
|
|
if(targetArray.variable.type==DataType.UWORD) {
|
2022-08-21 15:21:29 +00:00
|
|
|
// indexing a pointer var instead of a real array or string
|
|
|
|
if(itemsize!=1)
|
|
|
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
2023-03-13 20:53:02 +00:00
|
|
|
if(targetArray.index.type!=DataType.UBYTE)
|
2022-08-21 15:21:29 +00:00
|
|
|
throw AssemblyError("non-array var indexing requires bytes index")
|
2023-03-13 20:53:02 +00:00
|
|
|
val tr = expressionEval.translateExpression(targetArray.index)
|
|
|
|
val idxReg = tr.resultReg
|
|
|
|
addToResult(result, tr, tr.resultReg, -1)
|
2022-11-02 21:12:42 +00:00
|
|
|
val code = IRCodeChunk(null, null)
|
2022-08-21 15:21:29 +00:00
|
|
|
if(zero) {
|
|
|
|
// there's no STOREZIX instruction
|
2023-03-13 20:53:02 +00:00
|
|
|
valueRegister = codeGen.registers.nextFree()
|
|
|
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, value=0)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-03-13 20:53:02 +00:00
|
|
|
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
2022-10-06 22:34:56 +00:00
|
|
|
result += code
|
|
|
|
return result
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2023-03-13 20:53:02 +00:00
|
|
|
val fixedIndex = constIntValue(targetArray.index)
|
2022-08-21 15:21:29 +00:00
|
|
|
if(zero) {
|
|
|
|
if(fixedIndex!=null) {
|
2022-09-14 23:32:48 +00:00
|
|
|
val offset = fixedIndex*itemsize
|
2022-11-02 21:12:42 +00:00
|
|
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
2022-10-06 22:34:56 +00:00
|
|
|
result += chunk
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2022-09-30 13:27:03 +00:00
|
|
|
val indexReg = codeGen.registers.nextFree()
|
2023-03-13 20:53:02 +00:00
|
|
|
result += loadIndexReg(targetArray, itemsize, indexReg)
|
2022-11-02 21:12:42 +00:00
|
|
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-09-30 13:27:03 +00:00
|
|
|
if(vmDt== IRDataType.FLOAT) {
|
2022-08-21 15:21:29 +00:00
|
|
|
if(fixedIndex!=null) {
|
2022-09-14 23:32:48 +00:00
|
|
|
val offset = fixedIndex*itemsize
|
2023-03-13 20:53:02 +00:00
|
|
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
|
2022-10-06 22:34:56 +00:00
|
|
|
result += chunk
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2022-09-30 13:27:03 +00:00
|
|
|
val indexReg = codeGen.registers.nextFree()
|
2023-03-13 20:53:02 +00:00
|
|
|
result += loadIndexReg(targetArray, itemsize, indexReg)
|
|
|
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(fixedIndex!=null) {
|
2022-09-14 23:32:48 +00:00
|
|
|
val offset = fixedIndex*itemsize
|
2023-03-13 20:53:02 +00:00
|
|
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
|
2022-10-06 22:34:56 +00:00
|
|
|
result += chunk
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2022-09-30 13:27:03 +00:00
|
|
|
val indexReg = codeGen.registers.nextFree()
|
2023-03-13 20:53:02 +00:00
|
|
|
result += loadIndexReg(targetArray, itemsize, indexReg)
|
|
|
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-06 22:34:56 +00:00
|
|
|
return result
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-03-13 20:53:02 +00:00
|
|
|
else if(targetMemory!=null) {
|
|
|
|
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
2022-08-21 15:21:29 +00:00
|
|
|
if(zero) {
|
2023-03-13 20:53:02 +00:00
|
|
|
if(targetMemory.address is PtNumber) {
|
|
|
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
|
2022-10-06 22:34:56 +00:00
|
|
|
result += chunk
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
|
|
|
val addressReg = tr.resultReg
|
2023-03-13 03:16:50 +00:00
|
|
|
addToResult(result, tr, tr.resultReg, -1)
|
2023-03-13 20:53:02 +00:00
|
|
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
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()) }
|
2022-10-06 22:34:56 +00:00
|
|
|
result += chunk
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2023-03-13 20:53:02 +00:00
|
|
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
|
|
|
val addressReg = tr.resultReg
|
2023-03-13 03:16:50 +00:00
|
|
|
addToResult(result, tr, tr.resultReg, -1)
|
2023-03-13 20:53:02 +00:00
|
|
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-06 22:34:56 +00:00
|
|
|
|
|
|
|
return result
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
throw AssemblyError("weird assigntarget")
|
|
|
|
}
|
|
|
|
|
2022-10-06 22:34:56 +00:00
|
|
|
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)
|
2022-10-06 22:34:56 +00:00
|
|
|
} else {
|
2022-08-21 15:21:29 +00:00
|
|
|
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)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-03-13 01:21:58 +00:00
|
|
|
addToResult(result, tr, indexReg, -1)
|
|
|
|
return result
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|