add "-rpn" command line switch to transform exprs to RPN in codegen

This commit is contained in:
Irmen de Jong 2023-03-16 19:36:16 +01:00
parent 8ffca93cd5
commit 9241479da4
16 changed files with 166 additions and 105 deletions

View File

@ -53,7 +53,8 @@ sealed class PtNamedNode(var name: String, position: Position): PtNode(position)
class PtProgram(
val name: String,
val memsizer: IMemSizer,
val encoding: IStringEncoding
val encoding: IStringEncoding,
var binaryExpressionsAreRPN: Boolean = false
) : PtNode(Position.DUMMY) {
// fun allModuleDirectives(): Sequence<PtDirective> =
@ -68,7 +69,8 @@ class PtProgram(
// If the code generator wants, it can transform binary expression nodes into flat RPN nodes.
// This will destroy the original binaryexpression nodes!
fun transformBinExprToRPN() {
if(binaryExpressionsAreRPN)
return
fun transformToRPN(originalExpr: PtBinaryExpression): PtRpn {
fun makeRpn(expr: PtExpression): PtRpn {
val rpn = PtRpn(expr.type, expr.position)
@ -97,6 +99,7 @@ class PtProgram(
}
children.forEach { transformBinExprToRPN(it, this) }
binaryExpressionsAreRPN = true
}
}

View File

@ -170,12 +170,12 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
require(node is PtRpnOperator || node is PtExpression)
if(node is PtRpn) {
node.children.forEach {
children += it
children.add(it)
it.parent = this
}
}
else {
children += node
children.add(node)
node.parent = this
}
}
@ -264,19 +264,24 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
}
return Pair(maxDepths, numPushes)
}
fun finalOperator(): String? = (children.last() as? PtRpnOperator)?.operator
fun finalFirstOperand() = children[children.size-3]
fun finalSecondOperand() = children[children.size-2]
}
class PtRpnOperator(val operator: String, val type: DataType, val operand1Type: DataType, val operand2Type: DataType, position: Position): PtNode(position) {
// init {
// require(operand1Type equalsSize operand2Type) {
// "operator $operator : operand1 and 2 types aren't equal sizes: $operand1Type $operand2Type"
// }
// if(operator !in ComparisonOperators) {
// require(type equalsSize operand1Type && type equalsSize operand2Type) {
// "operator $operator : type $type size not equal to operand1 and 2 types: $operand1Type $operand2Type"
// }
// }
// }
init {
// NOTE: For now, we require that the types of the operands are the same size as the output type of the operator node.
require(operand1Type equalsSize operand2Type) {
"operand type size(s) differ: $operand1Type $operand2Type oper: $operator"
}
if(operator !in ComparisonOperators) {
require(type equalsSize operand1Type && type equalsSize operand2Type) {
"operand type size(s) differ from operator result type $type: $operand1Type $operand2Type oper: $operator"
}
}
}
}

View File

@ -101,7 +101,7 @@ class PtForLoop(position: Position) : PtNode(position) {
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtNode
get() = children[0] as PtNode
get() = children[0]
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup

View File

@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHigh: Boolean = false,
var useRPN: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()

View File

@ -21,8 +21,8 @@ class AsmGen6502: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
// If we want RPN expressions instead, use this:
// TODO program.transformBinExprToRPN()
if(options.useRPN)
program.transformBinExprToRPN()
val asmgen = AsmGen6502Internal(program, symbolTable, options, errors)
return asmgen.compileToAssembly()
@ -547,35 +547,38 @@ class AsmGen6502Internal (
}
private fun translate(stmt: PtIfElse) {
val condition = stmt.condition as PtBinaryExpression
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if(stmt.condition is PtRpn) {
TODO("RPN")
} else {
val condition = stmt.condition as PtBinaryExpression
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if(jump is PtJump) {
translateCompareAndJumpIfTrue(condition, jump)
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if (jump is PtJump) {
translateCompareAndJumpIfTrue(condition, jump)
} else {
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(condition, endLabel)
translate(stmt.ifScope)
out(endLabel)
}
} else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(condition, endLabel)
translateCompareAndJumpIfFalse(condition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
}
else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(condition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
}
private fun requireComparisonExpression(condition: PtExpression) {
if(condition !is PtBinaryExpression || condition.operator !in ComparisonOperators)
if (!(condition is PtRpn && condition.finalOperator() in ComparisonOperators) && !(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
throw AssemblyError("expected boolean comparison expression $condition")
}
@ -980,7 +983,11 @@ $repeatLabel lda $counterVar
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if(pointerOffsetExpr is PtBinaryExpression && pointerOffsetExpr.operator=="+") {
if (pointerOffsetExpr is PtRpn) {
TODO("RPN")
}
else if (pointerOffsetExpr is PtBinaryExpression) {
if (pointerOffsetExpr.operator != "+") return null
val leftDt = pointerOffsetExpr.left.type
val rightDt = pointerOffsetExpr.left.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
@ -1001,7 +1008,6 @@ $repeatLabel lda $counterVar
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
return Pair(pointerOffsetExpr.right, leftTc.value)
}
}
return null
}
@ -1022,7 +1028,6 @@ $repeatLabel lda $counterVar
}
}
if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) {

View File

@ -17,8 +17,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
errors: IErrorReporter
): IAssemblyProgram? {
// If we want RPN expressions instead, use this:
// program.transformBinExprToRPN()
if(options.useRPN)
program.transformBinExprToRPN()
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:

View File

@ -92,10 +92,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
value.add(origAssign.value)
} else {
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)
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN")
} else {
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)
}
}
normalAssign.add(value)
return translateRegularAssign(normalAssign)
@ -262,10 +266,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val tr = if(itemsize==1) {
expressionEval.translateExpression(array.index)
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
expressionEval.translateExpression(mult)
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN")
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
expressionEval.translateExpression(mult)
}
}
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)

View File

@ -898,57 +898,67 @@ class IRCodeGen(
private fun translate(ifElse: PtIfElse): IRCodeChunks {
val condition = ifElse.condition
if(condition is PtBinaryExpression) {
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
when (condition) {
is PtBinaryExpression -> {
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = condition.left.type in SignedDatatypes
val irDtLeft = irType(condition.left.type)
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
return when {
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
val signed = condition.left.type in SignedDatatypes
val irDtLeft = irType(condition.left.type)
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
return when {
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
}
}
is PtRpn -> {
TODO("RPN")
}
else -> {
TODO("weird condition node: $condition")
}
} else {
TODO("weird condition node: $condition")
}
}
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
val condition = ifElse.condition as PtBinaryExpression
if(irDtLeft==IRDataType.FLOAT) {
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg)
result += IRCodeChunk(null,null).also {
val compResultReg = registers.nextFree()
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
val gotoOpcode = when (condition.operator) {
"==" -> Opcode.BZ
"!=" -> Opcode.BNZ
"<" -> Opcode.BLEZS
">" -> Opcode.BGEZS
"<=" -> Opcode.BLZS
">=" -> Opcode.BGZS
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, value = goto.address?.toInt())
else if (goto.generatedLabel != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.generatedLabel)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name)
}
return result
if(program.binaryExpressionsAreRPN) {
TODO ("RPN")
} else {
val rightConst = condition.right.asConstInteger()
return if(rightConst==0)
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
else {
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
val result = mutableListOf<IRCodeChunkBase>()
val condition = ifElse.condition as PtBinaryExpression
if(irDtLeft==IRDataType.FLOAT) {
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg)
result += IRCodeChunk(null,null).also {
val compResultReg = registers.nextFree()
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
val gotoOpcode = when (condition.operator) {
"==" -> Opcode.BZ
"!=" -> Opcode.BNZ
"<" -> Opcode.BLEZS
">" -> Opcode.BGEZS
"<=" -> Opcode.BLZS
">=" -> Opcode.BGZS
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, value = goto.address?.toInt())
else if (goto.generatedLabel != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.generatedLabel)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name)
}
return result
} else {
val rightConst = condition.right.asConstInteger()
return if(rightConst==0)
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
else {
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
}
}
}
}
@ -960,6 +970,7 @@ class IRCodeGen(
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
require(!program.binaryExpressionsAreRPN)
val condition = ifElse.condition as PtBinaryExpression
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
@ -988,6 +999,7 @@ class IRCodeGen(
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
require(!program.binaryExpressionsAreRPN)
val condition = ifElse.condition as PtBinaryExpression
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
@ -1041,6 +1053,7 @@ class IRCodeGen(
}
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
require(!program.binaryExpressionsAreRPN)
val result = mutableListOf<IRCodeChunkBase>()
val elseBranch: Opcode
val compResultReg: Int
@ -1102,8 +1115,8 @@ class IRCodeGen(
}
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
require(!program.binaryExpressionsAreRPN)
val result = mutableListOf<IRCodeChunkBase>()
val elseBranchOpcode: Opcode
val elseBranchFirstReg: Int
val elseBranchSecondReg: Int

View File

@ -17,8 +17,10 @@ class VmCodeGen: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
// If we want RPN expressions instead, use this:
// program.transformBinExprToRPN()
if(options.useRPN)
program.transformBinExprToRPN()
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
return VmAssemblyProgram(irProgram.name, irProgram)

View File

@ -52,6 +52,7 @@ private fun compileMain(args: Array<String>): Boolean {
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
val useRPN by cli.option(ArgType.Boolean, fullName = "rpn", description = "use RPN for expression code-generation (experimental)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@ -126,6 +127,7 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
varsHigh == true,
useRPN == true,
compilationTarget,
evalStackAddr,
processedSymbols,
@ -190,6 +192,7 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
varsHigh == true,
useRPN == true,
compilationTarget,
evalStackAddr,
processedSymbols,

View File

@ -36,6 +36,7 @@ class CompilerArguments(val filepath: Path,
val asmListfile: Boolean,
val experimentalCodegen: Boolean,
val varsHigh: Boolean,
val useRPN: Boolean,
val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val symbolDefs: Map<String, String>,
@ -76,6 +77,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen
varsHigh = args.varsHigh
useRPN = args.useRPN
evalStackBaseAddress = args.evalStackBaseAddress
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs

View File

@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = false,
compilationTarget = target.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),

View File

@ -50,6 +50,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = false,
compilationTarget = Cx16Target.NAME,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),

View File

@ -31,6 +31,7 @@ internal fun compileFile(
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = false,
platform.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),

View File

@ -1,11 +1,26 @@
main {
uword[10] answers_animals
%import textio
%import test_stack
%zeropage basicsafe
%option no_sysinit
sub start() {
ubyte current_question = 1
uword previous_animals = 33
current_question = msb(answers_animals[current_question]) ; TODO takes 1 more vm registers than 8.10
answers_animals[current_question] = mkword(msb(previous_animals), 0) ; TODO takes 1 more vm registers than 8.10
; TODO expected result: 7 registers in 8.10, now takes 9 instead
main {
sub start() {
test_stack.test()
uword xx=32
cx16.r0L = 3
if cx16.r0L in "derp" {
xx++
}
xx = xx+(3*func(xx)+xx*2*cx16.r0L)
txt.print_uw(xx)
test_stack.test()
}
sub func(uword value) -> uword {
value ++
return value
}
}

View File

@ -42,7 +42,8 @@ class RequestParser : Take {
quietAssembler = false,
asmListfile = false,
experimentalCodegen = false,
varsHigh = false
varsHigh = false,
useRPN = false
)
val compilationResult = compileProgram(args)
return RsJson(Jsonding())