mirror of
https://github.com/irmen/prog8.git
synced 2024-07-26 05:29:15 +00:00
add "-rpn" command line switch to transform exprs to RPN in codegen
This commit is contained in:
parent
8ffca93cd5
commit
9241479da4
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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,6 +547,9 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtIfElse) {
|
||||
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>'
|
||||
|
||||
@ -560,8 +563,7 @@ class AsmGen6502Internal (
|
||||
translate(stmt.ifScope)
|
||||
out(endLabel)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// both true and else parts
|
||||
val elseLabel = makeLabel("if_else")
|
||||
val endLabel = makeLabel("if_end")
|
||||
@ -573,9 +575,10 @@ class AsmGen6502Internal (
|
||||
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) {
|
||||
|
@ -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:
|
||||
|
@ -92,11 +92,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
value.add(origAssign.value)
|
||||
} else {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
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)
|
||||
}
|
||||
@ -261,12 +265,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = if(itemsize==1) {
|
||||
expressionEval.translateExpression(array.index)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
@ -898,7 +898,8 @@ class IRCodeGen(
|
||||
|
||||
private fun translate(ifElse: PtIfElse): IRCodeChunks {
|
||||
val condition = ifElse.condition
|
||||
if(condition is PtBinaryExpression) {
|
||||
when (condition) {
|
||||
is PtBinaryExpression -> {
|
||||
if(condition.operator !in ComparisonOperators)
|
||||
throw AssemblyError("if condition should only be a binary comparison expression")
|
||||
|
||||
@ -910,12 +911,20 @@ class IRCodeGen(
|
||||
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
|
||||
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
is PtRpn -> {
|
||||
TODO("RPN")
|
||||
}
|
||||
else -> {
|
||||
TODO("weird condition node: $condition")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
|
||||
if(program.binaryExpressionsAreRPN) {
|
||||
TODO ("RPN")
|
||||
} else {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
if(irDtLeft==IRDataType.FLOAT) {
|
||||
@ -952,6 +961,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ifZeroIntThenJump(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -50,6 +50,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = false,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
|
@ -31,6 +31,7 @@ internal fun compileFile(
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = false,
|
||||
platform.name,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
|
@ -1,11 +1,26 @@
|
||||
%import textio
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
uword[10] answers_animals
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user