asmassignment can now use arbitrary source symbols; optimized byte-word sign extesion with this to not use stack anymore

This commit is contained in:
Irmen de Jong 2020-10-11 13:31:45 +02:00
parent 73524e01a6
commit c15fd75df7
10 changed files with 93 additions and 107 deletions

View File

@ -220,7 +220,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
programAst.processAstBeforeAsmGeneration(errors)
errors.handle()
// printAst(programAst)
printAst(programAst) // TODO
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
val assembly = CompilationTarget.instance.asmGenerator(

View File

@ -194,11 +194,10 @@ internal class AsmGen(private val program: Program,
}
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
val variable = IdentifierReference(variableName, decl.position)
variable.linkParents(decl.parent)
val asmName = asmVariableName(variableName)
val asgn = AsmAssignment(
AsmAssignSource.fromAstSource(decl.value!!, program),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variable = variable),
AsmAssignSource.fromAstSource(decl.value!!, program, this),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variableAsmName = asmName),
false, decl.position)
assignmentAsmGen.translateNormalAssignment(asgn)
}
@ -496,6 +495,12 @@ internal class AsmGen(private val program: Program,
}
}
internal fun asmSymbolName(name: String) = fixNameSymbols(name)
internal fun asmVariableName(name: String) = fixNameSymbols(name)
internal fun asmSymbolName(name: Iterable<String>) = fixNameSymbols(name.joinToString("."))
internal fun asmVariableName(name: Iterable<String>) = fixNameSymbols(name.joinToString("."))
internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> {
// returns if the pointer is already on the ZP itself or not (in the latter case SCRATCH_W1 is used as intermediary)
val sourceName = asmVariableName(pointervar)
@ -538,8 +543,7 @@ internal class AsmGen(private val program: Program,
}
}
internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
private val saveRegisterLabels = Stack<String>()

View File

@ -498,7 +498,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// all other types of swap() calls are done via the evaluation stack
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
return when (expr) {
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variable=expr)
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variableAsmName = asmgen.asmVariableName(expr))
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine(), array = expr)
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine(), memory = DirectMemoryWrite(expr.addressExpression, expr.position))
else -> throw AssemblyError("invalid expression object $expr")
@ -509,12 +509,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.translateExpression(second)
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
targetFromExpr(first, datatype),
false, first.position
)
val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
targetFromExpr(second, datatype),
false, second.position
)

View File

@ -610,8 +610,8 @@ $endLabel""")
}
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variable=stmt.loopVar)
val src = AsmAssignSource.fromAstSource(range.from, program).adjustSignedUnsigned(target)
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variableAsmName=asmgen.asmVariableName(stmt.loopVar))
val src = AsmAssignSource.fromAstSource(range.from, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(src, target, false, range.position)
asmgen.translateNormalAssignment(assign)
}

View File

@ -155,11 +155,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
throw AssemblyError("argument type incompatible")
val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".")
val identifier = IdentifierReference(scopedParamVar, sub.position)
identifier.linkParents(value.parent)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variable = identifier)
val source = AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(tgt)
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variableAsmName = varName)
val source = AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(tgt)
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(asgn)
}
@ -234,23 +232,25 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// via register or register pair
val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen)
if(requiredDt largerThan valueDt) {
// TODO we need to sign extend the source, do this via stack (slow)
println("warning: slow stack evaluation used for sign-extend: into $requiredDt at ${value.position}")
asmgen.translateExpression(value)
asmgen.signExtendStackLsb(valueDt)
val src = AsmAssignSource(SourceStorageKind.STACK, program, valueDt)
// we need to sign extend the source, do this via temporary word variable
val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1")
val scratchTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, sub, scratchVar)
val source = AsmAssignSource.fromAstSource(value, program, asmgen)
asmgen.translateNormalAssignment(AsmAssignment(source, scratchTarget, false, value.position))
asmgen.signExtendVariableLsb(scratchVar, valueDt)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UWORD, scratchVar)
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
}
else {
val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) {
val addr = AddressOf(value, Position.DUMMY)
AsmAssignSource.fromAstSource(addr, program).adjustSignedUnsigned(target)
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else {
AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(target)
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
} else {
AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(target)
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
}

View File

@ -31,11 +31,11 @@ internal enum class SourceStorageKind {
}
internal class AsmAssignTarget(val kind: TargetStorageKind,
program: Program,
asmgen: AsmGen,
private val program: Program,
private val asmgen: AsmGen,
val datatype: DataType,
val scope: Subroutine?,
val variable: IdentifierReference? = null,
private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryWrite? = null,
val register: RegisterOrPair? = null,
@ -44,19 +44,15 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
{
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
val vardecl by lazy { variable!!.targetVarDecl(program.namespace)!! }
val asmVarname by lazy {
if(variable!=null)
asmgen.asmVariableName(variable)
val asmVarname: String
get() = if(array==null)
variableAsmName!!
else
asmgen.asmVariableName(array!!.identifier)
}
asmgen.asmVariableName(array.identifier)
lateinit var origAssign: AsmAssignment
init {
if(variable!=null && vardecl.type == VarDeclType.CONST)
throw AssemblyError("can't assign to a constant")
if(register!=null && datatype !in IntegerDatatypes)
throw AssemblyError("register must be integer type")
}
@ -65,7 +61,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
when {
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variable=identifier, origAstTarget = this)
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine(), memory = memoryAddress, origAstTarget = this)
else -> throw AssemblyError("weird target")
@ -86,8 +82,9 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
internal class AsmAssignSource(val kind: SourceStorageKind,
private val program: Program,
private val asmgen: AsmGen,
val datatype: DataType,
val variable: IdentifierReference? = null,
private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryRead? = null,
val register: CpuRegister? = null,
@ -97,28 +94,33 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
{
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! }
val asmVarname: String
get() = if(array==null)
variableAsmName!!
else
asmgen.asmVariableName(array.identifier)
companion object {
fun fromAstSource(value: Expression, program: Program): AsmAssignSource {
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program)
if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, cv.type, number = cv)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
return when(value) {
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, value.type, number = cv)
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv)
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is IdentifierReference -> {
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
AsmAssignSource(SourceStorageKind.VARIABLE, program, dt, variable = value)
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value))
}
is DirectMemoryRead -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, DataType.UBYTE, memory = value)
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
}
is ArrayIndexedExpression -> {
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
AsmAssignSource(SourceStorageKind.ARRAY, program, dt, array = value)
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
}
else -> {
if(value is FunctionCall) {
@ -138,7 +140,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
RegisterOrPair.AY,
RegisterOrPair.XY -> DataType.UWORD
}
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value)
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
}
else -> throw AssemblyError("can't translate multiple return values in assignment")
}
@ -146,24 +148,14 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
}
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value)
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
}
}
}
}
fun getAstValue(): Expression = when(kind) {
SourceStorageKind.LITERALNUMBER -> number!!
SourceStorageKind.VARIABLE -> variable!!
SourceStorageKind.ARRAY -> array!!
SourceStorageKind.MEMORY -> memory!!
SourceStorageKind.EXPRESSION -> expression!!
SourceStorageKind.REGISTER -> throw AssemblyError("cannot get a register source as Ast node")
SourceStorageKind.STACK -> throw AssemblyError("cannot get a stack source as Ast node")
}
fun withAdjustedDt(newType: DataType) =
AsmAssignSource(kind, program, newType, variable, array, memory, register, number, expression)
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
// allow some signed/unsigned relaxations

View File

@ -18,7 +18,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
fun translate(assignment: Assignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program).adjustSignedUnsigned(target)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
target.origAssign = assign
@ -43,13 +43,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
SourceStorageKind.VARIABLE -> {
// simple case: assign from another variable
val variable = assign.source.variable!!
val variable = assign.source.asmVarname!!
when (assign.target.datatype) {
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable)
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
DataType.STR -> assignVariableString(assign.target, variable)
in PassByReferenceDatatypes -> assignAddressOf(assign.target, variable)
in PassByReferenceDatatypes -> {
// TODO what about when the name is a struct? name.firstStructVarName(program.namespace)
assignAddressOf(assign.target, variable)
}
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
}
}
@ -117,7 +120,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
SourceStorageKind.EXPRESSION -> {
val value = assign.source.expression!!
when(value) {
is AddressOf -> assignAddressOf(assign.target, value.identifier)
is AddressOf -> {
val sourceName = value.identifier.firstStructVarName(program.namespace) ?: asmgen.asmVariableName(value.identifier)
assignAddressOf(assign.target, sourceName)
}
is NumericLiteralValue -> 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")
@ -349,9 +355,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) {
val sourceName = name.firstStructVarName(program.namespace) ?: asmgen.fixNameSymbols(name.nameInSource.joinToString("."))
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@ -376,19 +380,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.STACK -> {
val srcname = asmgen.asmVariableName(name)
asmgen.out("""
lda #<$srcname
lda #<$sourceName
sta P8ESTACK_LO,x
lda #>$srcname
lda #>$sourceName
sta P8ESTACK_HI,x
dex""")
}
}
}
private fun assignVariableString(target: AsmAssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmVariableName(variable)
private fun assignVariableString(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
when(target.datatype) {
@ -425,8 +427,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignVariableWord(target: AsmAssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmVariableName(variable)
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@ -529,8 +530,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignVariableFloat(target: AsmAssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmVariableName(variable)
private fun assignVariableFloat(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@ -564,8 +564,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignVariableByte(target: AsmAssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmVariableName(variable)
private fun assignVariableByte(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""

View File

@ -7,16 +7,17 @@
; even in Vice in warp mode (700% speed on my machine) it's slow, but you can see progress
; Note: this program is compatible with C64 and CX16.
; TODO why is there a weird black column on the right part on CX16?
main {
const ubyte width = 255
const uword width = 320
const ubyte height = 200
const ubyte max_iter = 16
sub start() {
graphics.enable_bitmap_mode()
ubyte pixelx
uword pixelx
ubyte pixely
for pixely in 0 to height-1 {

View File

@ -7,29 +7,9 @@ main {
sub start() {
print_10s(1)
txt.chrout('\n')
print_10s(123)
txt.chrout('\n')
print_10s(54321)
txt.chrout('\n')
txt.chrout('\n')
ubyte ww
ww=1
print_10s(ww)
txt.chrout('\n')
ww=123
print_10s(ww)
txt.chrout('\n')
ww=255
print_10s(ww)
txt.chrout('\n')
txt.print("\nCommands are:\n"+
"buy jump inf cash\n" +
"sell teleport market hold\n" +
"fuel galhyp local quit\n")
ubyte xx
ubyte yy
regx(xx)
; str name = "irmen de jong"
; uword strptr = &name
@ -49,6 +29,14 @@ main {
}
asmsub regx(uword value @AX) {
%asm {{
nop
rts
}}
}
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
%asm {{
jsr conv.uword2decimal

View File

@ -25,6 +25,8 @@ main {
repeat {
str input = "????????"
txt.print("\nCash: ")
util.print_10s(ship.cash)
txt.print("\nCommand (?=help): ")
ubyte num_chars = txt.input_chars(input)
txt.chrout('\n')
@ -59,19 +61,19 @@ trader {
ubyte num_chars
sub do_jump() {
txt.print("\nTODO JUMP\n")
txt.print("\nTODO JUMP\n") ; TODO
}
sub do_teleport() {
txt.print("\nTODO TELEPORT\n")
txt.print("\nTODO TELEPORT\n") ; TODO
}
sub do_buy() {
txt.print("\nTODO BUY\n")
txt.print("\nTODO BUY\n") ; TODO
}
sub do_sell() {
txt.print("\nTODO SELL\n")
txt.print("\nTODO SELL\n") ; TODO
}
sub do_fuel() {
@ -88,7 +90,7 @@ trader {
}
sub do_hold() {
txt.print("\nCheat! TODO adjust cargo hold size\n")
txt.print("\nCheat! TODO adjust cargo hold size\n") ; TODO
}
sub do_next_galaxy() {
@ -101,7 +103,7 @@ trader {
txt.print("\nSystem name (empty=current): ")
num_chars = txt.input_chars(input)
if num_chars {
txt.print("\nTODO INFO\n")
txt.print("\nTODO INFO\n") ; TODO
} else {
planet.display(false)
}