mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
refactor
This commit is contained in:
parent
2f18a8f6d0
commit
c4fe3ecc0a
@ -36,7 +36,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val array: ArrayIndexedExpression? = null,
|
||||
val memory: DirectMemoryWrite? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val origAstTarget: AssignTarget? = null // TODO look into removing the need to store this
|
||||
val origAstTarget: AssignTarget? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
||||
|
@ -150,168 +150,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
when(val value = assign.source.expression!!) {
|
||||
is AddressOf -> {
|
||||
val sourceName = asmgen.asmSymbolName(value.identifier)
|
||||
assignAddressOf(assign.target, sourceName)
|
||||
}
|
||||
is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber")
|
||||
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
||||
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
||||
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
||||
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
|
||||
is FunctionCallExpression -> {
|
||||
val sub = value.target.targetSubroutine(program)!!
|
||||
asmgen.saveXbeforeCall(value)
|
||||
asmgen.translateFunctionCall(value, true)
|
||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
|
||||
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
|
||||
when (returnValue.first) {
|
||||
DataType.STR -> {
|
||||
asmgen.restoreXafterCall(value)
|
||||
when(assign.target.datatype) {
|
||||
DataType.UWORD -> {
|
||||
// assign the address of the string result value
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
// copy the actual string result into the target string variable
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
else -> throw AssemblyError("weird target dt")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
asmgen.restoreXafterCall(value)
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> {
|
||||
// do NOT restore X register before assigning the result values first
|
||||
when (returnValue.second.registerOrPair) {
|
||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
||||
else -> {
|
||||
val sflag = returnValue.second.statusflag
|
||||
if(sflag!=null)
|
||||
assignStatusFlagByte(assign.target, sflag)
|
||||
else
|
||||
throw AssemblyError("should be just one register byte result value")
|
||||
}
|
||||
}
|
||||
// we've processed the result value in the X register by now, so it's now finally safe to restore it
|
||||
asmgen.restoreXafterCall(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
is BuiltinFunctionCall -> {
|
||||
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
|
||||
if(assign.target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
val returntype = builtinFunctionReturnType(value.name, value.args, program)
|
||||
if(!returntype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
when(returntype.getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
DataType.STR -> {
|
||||
when (assign.target.datatype) {
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> throw AssemblyError("weird result type")
|
||||
}
|
||||
}
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
// first assign the value to the target then apply the operator in place on the target.
|
||||
translateNormalAssignment(AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.expression, program, asmgen),
|
||||
assign.target,
|
||||
false, program.memsizer, assign.position
|
||||
))
|
||||
val target = virtualRegsToVariables(assign.target)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
|
||||
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
|
||||
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
}
|
||||
is ContainmentCheck -> {
|
||||
containmentCheckIntoA(value)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
}
|
||||
is PipeExpression -> {
|
||||
asmgen.translatePipeExpression(value.expressions, value, false, false)
|
||||
val resultDt = value.inferType(program)
|
||||
val register =
|
||||
if(resultDt.isBytes) RegisterOrPair.A
|
||||
else if(resultDt.isWords) RegisterOrPair.AY
|
||||
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
||||
else throw AssemblyError("invalid dt")
|
||||
asmgen.assignRegister(register, assign.target)
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(value.operator in ComparisonOperators) {
|
||||
// TODO real optimized code for comparison expressions that yield a boolean result value
|
||||
// for now generate code for this: assign-false; if expr { assign-true }
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, DataType.UBYTE, number=NumericLiteral.fromBoolean(false, assign.position)),
|
||||
assign.target, false, program.memsizer, assign.position
|
||||
)
|
||||
)
|
||||
val origTarget = assign.target.origAstTarget
|
||||
if(origTarget!=null) {
|
||||
val assignTrue = AnonymousScope(mutableListOf(
|
||||
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||
), assign.position)
|
||||
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
|
||||
ifelse.linkParents(value)
|
||||
asmgen.translate(ifelse)
|
||||
}
|
||||
else {
|
||||
// no orig ast assign target so can't use the workaround, so fallback to stack eval
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
} else {
|
||||
// All remaining binary expressions just evaluate via the stack for now.
|
||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||
// because the code here is the implementation of exactly that...)
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird assignment value type $value")
|
||||
}
|
||||
assignExpression(assign)
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
asmgen.assignRegister(assign.source.register!!, assign.target)
|
||||
@ -323,6 +162,165 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignExpression(assign: AsmAssignment) {
|
||||
when(val value = assign.source.expression!!) {
|
||||
is AddressOf -> {
|
||||
val sourceName = asmgen.asmSymbolName(value.identifier)
|
||||
assignAddressOf(assign.target, sourceName)
|
||||
}
|
||||
is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber")
|
||||
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
||||
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
||||
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
||||
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
|
||||
is FunctionCallExpression -> {
|
||||
val sub = value.target.targetSubroutine(program)!!
|
||||
asmgen.saveXbeforeCall(value)
|
||||
asmgen.translateFunctionCall(value, true)
|
||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
|
||||
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
|
||||
when (returnValue.first) {
|
||||
DataType.STR -> {
|
||||
asmgen.restoreXafterCall(value)
|
||||
when(assign.target.datatype) {
|
||||
DataType.UWORD -> {
|
||||
// assign the address of the string result value
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
// copy the actual string result into the target string variable
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
else -> throw AssemblyError("weird target dt")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
asmgen.restoreXafterCall(value)
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> {
|
||||
// do NOT restore X register before assigning the result values first
|
||||
when (returnValue.second.registerOrPair) {
|
||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
||||
else -> {
|
||||
val sflag = returnValue.second.statusflag
|
||||
if(sflag!=null)
|
||||
assignStatusFlagByte(assign.target, sflag)
|
||||
else
|
||||
throw AssemblyError("should be just one register byte result value")
|
||||
}
|
||||
}
|
||||
// we've processed the result value in the X register by now, so it's now finally safe to restore it
|
||||
asmgen.restoreXafterCall(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
is BuiltinFunctionCall -> {
|
||||
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
|
||||
if(assign.target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
val returntype = builtinFunctionReturnType(value.name, value.args, program)
|
||||
if(!returntype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
when(returntype.getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
DataType.STR -> {
|
||||
when (assign.target.datatype) {
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> throw AssemblyError("weird result type")
|
||||
}
|
||||
}
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
// first assign the value to the target then apply the operator in place on the target.
|
||||
translateNormalAssignment(AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.expression, program, asmgen),
|
||||
assign.target,
|
||||
false, program.memsizer, assign.position
|
||||
))
|
||||
val target = virtualRegsToVariables(assign.target)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
|
||||
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
|
||||
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
}
|
||||
is ContainmentCheck -> {
|
||||
containmentCheckIntoA(value)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
}
|
||||
is PipeExpression -> {
|
||||
asmgen.translatePipeExpression(value.expressions, value, false, false)
|
||||
val resultDt = value.inferType(program)
|
||||
val register =
|
||||
if(resultDt.isBytes) RegisterOrPair.A
|
||||
else if(resultDt.isWords) RegisterOrPair.AY
|
||||
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
||||
else throw AssemblyError("invalid dt")
|
||||
asmgen.assignRegister(register, assign.target)
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(value.operator in ComparisonOperators) {
|
||||
// TODO real optimized code for comparison expressions that yield a boolean result value
|
||||
assignConstantByte(assign.target, 0)
|
||||
val origTarget = assign.target.origAstTarget
|
||||
if(origTarget!=null) {
|
||||
val assignTrue = AnonymousScope(mutableListOf(
|
||||
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||
), assign.position)
|
||||
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
|
||||
ifelse.linkParents(value)
|
||||
asmgen.translate(ifelse)
|
||||
}
|
||||
else {
|
||||
// no orig ast assign target so can't use the workaround, so fallback to stack eval
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
} else {
|
||||
// All remaining binary expressions just evaluate via the stack for now.
|
||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||
// because the code here is the implementation of exactly that...)
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird assignment value type $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackToStackEval(assign: AsmAssignment) {
|
||||
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
|
||||
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.
|
||||
|
@ -98,14 +98,14 @@ X = BinExpr X = LeftExpr
|
||||
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
||||
// (that has the type of the expression), and then finally doing the typecast.
|
||||
// Once it's outside the typecast, the regular splitting can commence.
|
||||
val tempVar = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED))
|
||||
val (tempVarName, _) = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED))
|
||||
val assignTempVar = Assignment(
|
||||
AssignTarget(IdentifierReference(tempVar, typecast.position), null, null, typecast.position),
|
||||
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
|
||||
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignTempVar, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVar, typecast.position), typecast)
|
||||
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVarName, typecast.position), typecast)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,6 @@ package prog8.optimizer
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.compilerinterface.CompilationOptions
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
@ -68,15 +65,3 @@ fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICom
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
fun getTempRegisterName(dt: InferredTypes.InferredType): List<String> {
|
||||
return when {
|
||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
|
||||
dt istype DataType.UBYTE -> listOf("cx16", "r9L")
|
||||
dt istype DataType.BYTE -> listOf("cx16", "r9sL")
|
||||
dt istype DataType.UWORD -> listOf("cx16", "r9")
|
||||
dt istype DataType.WORD -> listOf("cx16", "r9s")
|
||||
dt.isPassByReference -> listOf("cx16", "r9")
|
||||
else -> throw FatalAstException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
|
||||
val arg = functionCallStatement.args[0]
|
||||
if(!arg.isSimple && arg !is IFunctionCall) {
|
||||
val name = getTempRegisterName(arg.inferType(program))
|
||||
val name = program.getTempRegisterName(arg.inferType(program))
|
||||
val tempvar = IdentifierReference(name, functionCallStatement.position)
|
||||
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position)
|
||||
return listOf(
|
||||
@ -474,7 +474,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val returnDt = subr.returntypes.single()
|
||||
if (returnDt in IntegerDatatypes) {
|
||||
// first assign to intermediary variable, then return that
|
||||
val returnVarName = program.getTempVar(returnDt)
|
||||
val (returnVarName, _) = program.getTempVar(returnDt)
|
||||
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
||||
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
||||
|
@ -8,7 +8,6 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compilerinterface.*
|
||||
import prog8.optimizer.getTempRegisterName
|
||||
|
||||
internal class BeforeAsmAstChanger(val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
@ -242,7 +241,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
}
|
||||
|
||||
if(separateLeftExpr) {
|
||||
val name = getTempRegisterName(leftDt)
|
||||
val name = program.getTempRegisterName(leftDt)
|
||||
leftOperandReplacement = IdentifierReference(name, expr.position)
|
||||
leftAssignment = Assignment(
|
||||
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
|
||||
@ -251,12 +250,11 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
)
|
||||
}
|
||||
if(separateRightExpr) {
|
||||
val name = program.getTempVar(rightDt.getOrElse { throw FatalAstException("invalid dt") }, true)
|
||||
val tempvardecl = program.toplevelModule.lookup(name) as VarDecl
|
||||
variables.addIfUnknown(tempvardecl.definingBlock, tempvardecl)
|
||||
rightOperandReplacement = IdentifierReference(name, expr.position)
|
||||
val (tempVarName, tempVarDecl) = program.getTempVar(rightDt.getOrElse { throw FatalAstException("invalid dt") }, true)
|
||||
variables.addIfUnknown(tempVarDecl.definingBlock, tempVarDecl)
|
||||
rightOperandReplacement = IdentifierReference(tempVarName, expr.position)
|
||||
rightAssignment = Assignment(
|
||||
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
|
||||
AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position),
|
||||
expr.right,
|
||||
AssignmentOrigin.BEFOREASMGEN, expr.position
|
||||
)
|
||||
@ -325,10 +323,9 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val statement = expr.containingStatement
|
||||
val dt = expr.indexer.indexExpr.inferType(program)
|
||||
val tempvar = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") })
|
||||
val tempvardecl = program.toplevelModule.lookup(tempvar) as VarDecl
|
||||
variables.addIfUnknown(tempvardecl.definingBlock, tempvardecl)
|
||||
val target = AssignTarget(IdentifierReference(tempvar, expr.indexer.position), null, null, expr.indexer.position)
|
||||
val (tempVarName, tempVarDecl) = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") })
|
||||
variables.addIfUnknown(tempVarDecl.definingBlock, tempVarDecl)
|
||||
val target = AssignTarget(IdentifierReference(tempVarName, expr.indexer.position), null, null, expr.indexer.position)
|
||||
val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.BEFOREASMGEN, expr.indexer.position)
|
||||
modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer))
|
||||
modifications.add(
|
||||
|
@ -14,10 +14,6 @@ import prog8.parser.SourceCode
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
|
||||
interface IAssignable {
|
||||
// just a tag for now
|
||||
}
|
||||
|
||||
interface IFunctionCall {
|
||||
var target: IdentifierReference
|
||||
var args: MutableList<Expression>
|
||||
|
@ -4,6 +4,7 @@ import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclOrigin
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
@ -37,7 +38,7 @@ fun UInt.toHex(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun Program.getTempVar(dt: DataType, altNames: Boolean=false): List<String> {
|
||||
fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>, VarDecl> {
|
||||
val tmpvarName = if(altNames) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> listOf("prog8_lib", "tempvar_ub2")
|
||||
@ -59,8 +60,9 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): List<String> {
|
||||
}
|
||||
|
||||
val block = this.allBlocks.first { it.name==tmpvarName[0] }
|
||||
if(block.statements.filterIsInstance<VarDecl>().any { it.name == tmpvarName[1] })
|
||||
return tmpvarName
|
||||
val existingDecl = block.statements.firstOrNull { it is VarDecl && it.name == tmpvarName[1] }
|
||||
if(existingDecl!=null)
|
||||
return Pair(tmpvarName, existingDecl as VarDecl)
|
||||
|
||||
// add new temp variable to the ast directly (we can do this here because we're not iterating inside those container blocks)
|
||||
val decl = VarDecl(
|
||||
@ -68,5 +70,17 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): List<String> {
|
||||
null, tmpvarName[1], null, false, false, null, Position.DUMMY)
|
||||
block.statements.add(decl)
|
||||
decl.linkParents(block)
|
||||
return tmpvarName
|
||||
return Pair(tmpvarName, decl)
|
||||
}
|
||||
|
||||
fun Program.getTempRegisterName(dt: InferredTypes.InferredType): List<String> {
|
||||
return when {
|
||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
|
||||
dt istype DataType.UBYTE -> listOf("cx16", "r9L")
|
||||
dt istype DataType.BYTE -> listOf("cx16", "r9sL")
|
||||
dt istype DataType.UWORD -> listOf("cx16", "r9")
|
||||
dt istype DataType.WORD -> listOf("cx16", "r9s")
|
||||
dt.isPassByReference -> listOf("cx16", "r9")
|
||||
else -> throw FatalAstException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
||||
|
||||
class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||
val indexer: ArrayIndex,
|
||||
override val position: Position) : Expression(), IAssignable {
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
@ -393,7 +393,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||
}
|
||||
|
||||
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression(), IAssignable {
|
||||
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@ -847,7 +847,7 @@ class RangeExpression(var from: Expression,
|
||||
}
|
||||
|
||||
|
||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override val isSimple = true
|
||||
|
@ -12,6 +12,8 @@ import prog8.ast.statements.ZeropageWish
|
||||
/**
|
||||
* A more convenient way to pass variable (and constant values) definitions to the code generator,
|
||||
* so that it doesn't have to scavenge and manipulate the VerDecl nodes in the AST for this information.
|
||||
*
|
||||
* note: the string variables are in here as well, they're in blockVars for the block named 'prog8_interned_strings'.
|
||||
*/
|
||||
interface IVariablesAndConsts {
|
||||
data class ConstantNumberSymbol(val type: DataType, val scopedname: List<String>, val value: Double, val position: Position)
|
||||
@ -31,5 +33,8 @@ interface IVariablesAndConsts {
|
||||
val subroutineConsts: Map<Subroutine, Set<ConstantNumberSymbol>>
|
||||
val subroutineMemvars: Map<Subroutine, Set<MemoryMappedVariable>>
|
||||
|
||||
/**
|
||||
* ability to add another new variable after the tables have already been created.
|
||||
*/
|
||||
fun addIfUnknown(definingBlock: Block, variable: VarDecl)
|
||||
}
|
||||
|
@ -3,8 +3,7 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- see if we can get rid of storing the origAstTarget in AsmAssignTarget
|
||||
|
||||
...
|
||||
|
||||
Need help with
|
||||
^^^^^^^^^^^^^^
|
||||
@ -24,8 +23,8 @@ Future Things and Ideas
|
||||
Compiler:
|
||||
|
||||
- writeAssembly(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
|
||||
- make everything an expression. (get rid of Statements. Statements are expressions with void return types).
|
||||
- allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type
|
||||
- unify FunctioncallExpression + FunctioncallStatement and PipeExpression + Pipe statement classes, may require moving Expression/Statement into interfaces instead of abstract base classes
|
||||
- for the pipe operator: recognise a placeholder (``?`` or ``%`` or ``_``) in a non-unary function call to allow non-unary functions in the chain; ``4 |> mkword(?, $44) |> print_uw``
|
||||
OR: change pipe syntax and require function call, but always have implicit first argument added.
|
||||
- for the pipe operator: make it 100% syntactic sugar so there's no need for asm codegen like translatePipeExpression
|
||||
@ -73,3 +72,4 @@ Optimizations:
|
||||
but we have no efficient way right now to see if the body references a variable.
|
||||
- automatically convert if statements that test for multiple values (if X==1 or X==2..) to if X in [1,2,..] statements, instead of just a warning.
|
||||
- introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4
|
||||
- AssignmentAsmGen: "real optimized code for comparison expressions that yield a boolean result value"
|
||||
|
@ -4,6 +4,13 @@ main {
|
||||
sub start() {
|
||||
ubyte xx = 200
|
||||
ubyte yy = 100
|
||||
uword @shared cc
|
||||
|
||||
ubyte[200] array
|
||||
|
||||
cc=array[xx+yy+10]
|
||||
|
||||
cc = xx-yy>10
|
||||
|
||||
uword qq = 100
|
||||
cmp(qq,xx)
|
||||
|
Loading…
x
Reference in New Issue
Block a user