2022-08-21 15:21:29 +00:00
|
|
|
package prog8.codegen.experimental
|
|
|
|
|
|
|
|
import prog8.code.ast.*
|
2022-08-27 11:06:44 +00:00
|
|
|
import prog8.code.core.AssemblyError
|
|
|
|
import prog8.code.core.DataType
|
|
|
|
import prog8.code.core.Position
|
|
|
|
import prog8.code.core.SignedDatatypes
|
|
|
|
import prog8.intermediate.IRCodeChunk
|
|
|
|
import prog8.intermediate.IRCodeInstruction
|
|
|
|
import prog8.intermediate.Opcode
|
|
|
|
import prog8.intermediate.VmDataType
|
2022-08-21 15:21:29 +00:00
|
|
|
|
|
|
|
internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) {
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
internal fun translate(assignment: PtAssignment): IRCodeChunk {
|
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")
|
|
|
|
|
|
|
|
return if (assignment.isInplaceAssign)
|
|
|
|
translateInplaceAssign(assignment)
|
|
|
|
else
|
|
|
|
translateRegularAssign(assignment)
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunk {
|
2022-08-21 15:21:29 +00:00
|
|
|
val ident = assignment.target.identifier
|
|
|
|
val memory = assignment.target.memory
|
|
|
|
val array = assignment.target.array
|
|
|
|
|
|
|
|
return if(ident!=null) {
|
2022-09-04 21:18:35 +00:00
|
|
|
val address = codeGen.addressOf(ident.targetName)
|
2022-08-21 15:21:29 +00:00
|
|
|
assignSelfInMemory(address, assignment.value, assignment)
|
|
|
|
} else if(memory != null) {
|
|
|
|
if(memory.address is PtNumber)
|
|
|
|
assignSelfInMemory((memory.address as PtNumber).number.toInt(), assignment.value, 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 assignSelfInMemory(
|
|
|
|
address: Int,
|
|
|
|
value: PtExpression,
|
|
|
|
origAssign: PtAssignment
|
2022-08-25 20:38:53 +00:00
|
|
|
): IRCodeChunk {
|
2022-08-21 15:21:29 +00:00
|
|
|
val vmDt = codeGen.vmType(value.type)
|
2022-08-25 20:38:53 +00:00
|
|
|
val code = IRCodeChunk(origAssign.position)
|
2022-08-21 15:21:29 +00:00
|
|
|
when(value) {
|
|
|
|
is PtIdentifier -> return code // do nothing, x=x null assignment.
|
|
|
|
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
|
2022-08-25 19:02:18 +00:00
|
|
|
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, value.position)
|
2022-08-21 15:21:29 +00:00
|
|
|
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, origAssign)
|
|
|
|
is PtMemoryByte -> {
|
|
|
|
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
|
|
|
|
code // do nothing, mem=mem null assignment.
|
|
|
|
else {
|
|
|
|
// read and write a (i/o) memory location to itself.
|
|
|
|
val tempReg = codeGen.vmRegisters.nextFree()
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
|
|
|
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else -> return fallbackAssign(origAssign)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunk {
|
2022-08-21 15:21:29 +00:00
|
|
|
if (codeGen.options.slowCodegenWarnings)
|
|
|
|
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
|
|
|
return translateRegularAssign(origAssign)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun inplaceBinexpr(
|
|
|
|
operator: String,
|
|
|
|
operand: PtExpression,
|
|
|
|
vmDt: VmDataType,
|
|
|
|
signed: Boolean,
|
|
|
|
address: Int,
|
|
|
|
origAssign: PtAssignment
|
2022-08-25 20:38:53 +00:00
|
|
|
): IRCodeChunk {
|
2022-08-21 15:21:29 +00:00
|
|
|
when(operator) {
|
|
|
|
"+" -> return expressionEval.operatorPlusInplace(address, vmDt, operand)
|
|
|
|
"-" -> return expressionEval.operatorMinusInplace(address, vmDt, operand)
|
|
|
|
"*" -> return expressionEval.operatorMultiplyInplace(address, vmDt, operand)
|
|
|
|
"/" -> return expressionEval.operatorDivideInplace(address, vmDt, signed, operand)
|
|
|
|
"|" -> return expressionEval.operatorOrInplace(address, vmDt, operand)
|
|
|
|
"&" -> return expressionEval.operatorAndInplace(address, vmDt, operand)
|
|
|
|
"^" -> return expressionEval.operatorXorInplace(address, vmDt, operand)
|
|
|
|
"<<" -> return expressionEval.operatorShiftLeftInplace(address, vmDt, operand)
|
|
|
|
">>" -> return expressionEval.operatorShiftRightInplace(address, vmDt, signed, operand)
|
|
|
|
else -> {}
|
|
|
|
}
|
|
|
|
return fallbackAssign(origAssign)
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
private fun inplacePrefix(operator: String, vmDt: VmDataType, address: Int, position: Position): IRCodeChunk {
|
|
|
|
val code= IRCodeChunk(position)
|
2022-08-21 15:21:29 +00:00
|
|
|
when(operator) {
|
|
|
|
"+" -> { }
|
|
|
|
"-" -> {
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.NEGM, vmDt, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
"~" -> {
|
|
|
|
val regMask = codeGen.vmRegisters.nextFree()
|
|
|
|
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
|
|
|
code += IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
else -> throw AssemblyError("weird prefix operator")
|
|
|
|
}
|
|
|
|
return code
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunk {
|
2022-08-21 15:21:29 +00:00
|
|
|
// 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.vmType(assignment.value.type)
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
val code = IRCodeChunk(assignment.position)
|
2022-08-21 15:21:29 +00:00
|
|
|
var resultRegister = -1
|
|
|
|
var resultFpRegister = -1
|
|
|
|
val zero = codeGen.isZero(assignment.value)
|
|
|
|
if(!zero) {
|
|
|
|
// calculate the assignment value
|
|
|
|
if (vmDt == VmDataType.FLOAT) {
|
|
|
|
resultFpRegister = codeGen.vmRegisters.nextFreeFloat()
|
|
|
|
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
|
|
|
|
} else {
|
|
|
|
resultRegister = if (assignment.value is PtMachineRegister) {
|
|
|
|
(assignment.value as PtMachineRegister).register
|
|
|
|
} else {
|
|
|
|
val reg = codeGen.vmRegisters.nextFree()
|
|
|
|
code += expressionEval.translateExpression(assignment.value, reg, -1)
|
|
|
|
reg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(ident!=null) {
|
2022-09-04 21:18:35 +00:00
|
|
|
val address = codeGen.addressOf(ident.targetName)
|
2022-08-21 15:21:29 +00:00
|
|
|
code += if(zero) {
|
2022-08-25 20:38:53 +00:00
|
|
|
IRCodeInstruction(Opcode.STOREZM, vmDt, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
if (vmDt == VmDataType.FLOAT)
|
2022-08-25 20:38:53 +00:00
|
|
|
IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
else
|
2022-08-25 20:38:53 +00:00
|
|
|
IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value = address)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(array!=null) {
|
|
|
|
val variable = array.variable.targetName
|
2022-09-04 21:18:35 +00:00
|
|
|
var variableAddr = codeGen.addressOf(variable)
|
2022-08-21 15:21:29 +00:00
|
|
|
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")
|
|
|
|
val idxReg = codeGen.vmRegisters.nextFree()
|
|
|
|
code += expressionEval.translateExpression(array.index, idxReg, -1)
|
|
|
|
if(zero) {
|
|
|
|
// there's no STOREZIX instruction
|
|
|
|
resultRegister = codeGen.vmRegisters.nextFree()
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, value = variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
return code
|
|
|
|
}
|
|
|
|
|
|
|
|
val fixedIndex = constIntValue(array.index)
|
|
|
|
if(zero) {
|
|
|
|
if(fixedIndex!=null) {
|
|
|
|
variableAddr += fixedIndex*itemsize
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
val indexReg = codeGen.vmRegisters.nextFree()
|
2022-08-25 19:02:18 +00:00
|
|
|
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(vmDt== VmDataType.FLOAT) {
|
|
|
|
if(fixedIndex!=null) {
|
|
|
|
variableAddr += fixedIndex*itemsize
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
val indexReg = codeGen.vmRegisters.nextFree()
|
2022-08-25 19:02:18 +00:00
|
|
|
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(fixedIndex!=null) {
|
|
|
|
variableAddr += fixedIndex*itemsize
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
val indexReg = codeGen.vmRegisters.nextFree()
|
2022-08-25 19:02:18 +00:00
|
|
|
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(memory!=null) {
|
|
|
|
require(vmDt== VmDataType.BYTE)
|
|
|
|
if(zero) {
|
|
|
|
if(memory.address is PtNumber) {
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
val addressReg = codeGen.vmRegisters.nextFree()
|
|
|
|
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(memory.address is PtNumber) {
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
|
|
|
val addressReg = codeGen.vmRegisters.nextFree()
|
|
|
|
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
2022-08-25 20:38:53 +00:00
|
|
|
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw AssemblyError("weird assigntarget")
|
|
|
|
return code
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:38:53 +00:00
|
|
|
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int, position: Position): IRCodeChunk {
|
|
|
|
val code = IRCodeChunk(position)
|
2022-08-21 15:21:29 +00:00
|
|
|
if(itemsize==1) {
|
|
|
|
code += expressionEval.translateExpression(array.index, indexReg, -1)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
|
|
|
mult.children += array.index
|
|
|
|
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
|
|
|
code += expressionEval.translateExpression(mult, indexReg, -1)
|
|
|
|
}
|
|
|
|
return code
|
|
|
|
}
|
|
|
|
}
|