better return types

This commit is contained in:
Irmen de Jong 2021-10-12 21:59:19 +02:00
parent f891fc698c
commit 4f7465ba44
4 changed files with 39 additions and 18 deletions

View File

@ -100,12 +100,13 @@ fun compileProgram(filepath: Path,
// printAst(programAst)
if(writeAssembly) {
val (success, message) = writeAssembly(programAst, errors, outputDir, compilationOptions)
if(success)
programName = message
else {
System.err.println(message)
return CompilationResult(false, programAst, programName, compTarget, importedFiles)
val result = writeAssembly(programAst, errors, outputDir, compilationOptions)
when(result) {
is WriteAssemblyResult.Ok -> programName = result.filename
is WriteAssemblyResult.Fail -> {
System.err.println(result.error)
return CompilationResult(false, programAst, programName, compTarget, importedFiles)
}
}
}
}
@ -319,10 +320,15 @@ private fun postprocessAst(programAst: Program, errors: IErrorReporter, compiler
programAst.moveMainAndStartToFirst()
}
private sealed class WriteAssemblyResult {
class Ok(val filename: String): WriteAssemblyResult()
class Fail(val error: String): WriteAssemblyResult()
}
private fun writeAssembly(programAst: Program,
errors: IErrorReporter,
outputDir: Path,
compilerOptions: CompilationOptions): Pair<Boolean, String> {
compilerOptions: CompilationOptions): WriteAssemblyResult {
// asm generation directly from the Ast
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
errors.report()
@ -340,14 +346,14 @@ private fun writeAssembly(programAst: Program,
return if(assembly.valid && errors.noErrors()) {
val assemblerReturnStatus = assembly.assemble(compilerOptions)
if(assemblerReturnStatus!=0)
Pair(false, "assembler step failed with return code $assemblerReturnStatus")
WriteAssemblyResult.Fail("assembler step failed with return code $assemblerReturnStatus")
else {
errors.report()
Pair(true, assembly.name)
WriteAssemblyResult.Ok(assembly.name)
}
} else {
errors.report()
Pair(false, "compiler failed with errors")
WriteAssemblyResult.Fail("compiler failed with errors")
}
}

View File

@ -30,9 +30,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, keepAonEntry)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
}
@ -42,10 +41,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, keepAonReturn)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}

View File

@ -0,0 +1,15 @@
package prog8
sealed class Either<out L, out R> {
data class Left<out L>(val value: L) : Either<L, Nothing>()
data class Right<out R>(val value: R) : Either<Nothing, R>()
fun isRight() = this is Right<R>
fun isLeft() = this is Left<L>
}
fun <L> left(a: L) = Either.Left(a)
fun <R> right(b: R) = Either.Right(b)

View File

@ -651,15 +651,18 @@ class Subroutine(override val name: String,
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
fun shouldKeepA(): Pair<Boolean, Boolean> {
class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
fun shouldKeepA(): KeepAresult {
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
if(!isAsmSubroutine)
return Pair(false, false)
return KeepAresult(saveOnEntry = false, saveOnReturn = false)
// it seems that we never have to save A when calling? will be loaded correctly after setup.
// but on return it depends on wether the routine returns something in A.
val saveAonReturn = asmReturnvaluesRegisters.any { it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.AX }
return Pair(false, saveAonReturn)
return KeepAresult(false, saveAonReturn)
}
fun amountOfRtsInAsm(): Int = statements