mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
stuff
This commit is contained in:
parent
75b38d7b84
commit
25e44a54fb
31
build_the_compiler.sh
Executable file
31
build_the_compiler.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Compiling the parser..."
|
||||
java -jar ./parser/antlr/lib/antlr-4.7.2-complete.jar -o ./parser/src/prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ./parser/antlr/prog8.g4
|
||||
|
||||
|
||||
PARSER_CLASSES=./out/production/parser
|
||||
COMPILER_JAR=prog8compiler.jar
|
||||
ANTLR_RUNTIME=./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
||||
|
||||
mkdir -p ${PARSER_CLASSES}
|
||||
javac -d ${PARSER_CLASSES} -cp ${ANTLR_RUNTIME} ./parser/src/prog8/parser/prog8Lexer.java ./parser/src/prog8/parser/prog8Parser.java
|
||||
|
||||
echo "Compiling the compiler itself..."
|
||||
kotlinc -verbose -include-runtime -d ${COMPILER_JAR} -cp ${ANTLR_RUNTIME}:${PARSER_CLASSES} ./compiler/src/prog8
|
||||
|
||||
echo "Finalizing the compiler jar file..."
|
||||
# add the antlr parser classes
|
||||
jar ufe ${COMPILER_JAR} prog8.CompilerMainKt -C ${PARSER_CLASSES} prog8
|
||||
|
||||
# add the resources
|
||||
jar uf ${COMPILER_JAR} -C ./compiler/res .
|
||||
|
||||
# add the antlr runtime classes
|
||||
rm -rf antlr_runtime_extraction
|
||||
mkdir antlr_runtime_extraction
|
||||
(cd antlr_runtime_extraction; jar xf ../${ANTLR_RUNTIME})
|
||||
jar uf ${COMPILER_JAR} -C antlr_runtime_extraction org
|
||||
rm -rf antlr_runtime_extraction
|
||||
|
||||
echo "Done!"
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
java -jar ../antlr/lib/antlr-4.7.2-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
|
||||
|
||||
find prog8 -name \*.java > javasources.txt
|
||||
mkdir -p compiled_java
|
||||
javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.2.jar @javasources.txt
|
||||
rm javasources.txt
|
||||
|
||||
KOTLINC="bash ${HOME}/.IntelliJIdea2018.3/config/plugins/Kotlin/kotlinc/bin/kotlinc"
|
||||
${KOTLINC} -verbose -include-runtime -d prog8_kotlin.jar -cp ../antlr/lib/antlr-runtime-4.7.2.jar:compiled_java prog8
|
||||
|
||||
jar uf prog8_kotlin.jar -C compiled_java prog8
|
@ -8,6 +8,7 @@ import prog8.optimizing.optimizeStatements
|
||||
import prog8.optimizing.simplifyExpressions
|
||||
import prog8.parser.ParsingFailedError
|
||||
import prog8.parser.importModule
|
||||
import prog8.stackvm.StackVm
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.lang.Exception
|
||||
@ -17,6 +18,15 @@ import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
// check if the user wants to launch the VM instead
|
||||
if("--vm" in args) {
|
||||
val newArgs = args.toMutableList()
|
||||
newArgs.remove("--vm")
|
||||
return stackVmMain(newArgs.toTypedArray())
|
||||
}
|
||||
|
||||
|
||||
println("\nProg8 compiler by Irmen de Jong (irmen@razorvine.net)")
|
||||
// @todo software license string
|
||||
// println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||
@ -169,7 +179,7 @@ private fun compileMain(args: Array<String>) {
|
||||
|
||||
if(startEmu) {
|
||||
println("\nStarting C64 emulator...")
|
||||
val cmdline = listOf("x64", "-silent", "-moncommands", "$programname.vice-mon-list",
|
||||
val cmdline = listOf("x64sc", "-silent", "-moncommands", "$programname.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programname+".prg")
|
||||
val process = ProcessBuilder(cmdline).inheritIO().start()
|
||||
process.waitFor()
|
||||
@ -180,6 +190,7 @@ private fun usage() {
|
||||
System.err.println("Missing argument(s):")
|
||||
System.err.println(" [--emu] auto-start the C64 emulator after successful compilation")
|
||||
System.err.println(" [--asmtrace] print trace output of the AsmGen for debugging purposes")
|
||||
System.err.println(" [--vm] launch the prog8 virtual machine instead of the compiler")
|
||||
System.err.println(" modulefile main module file to compile")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ import javax.swing.Timer
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
stackVmMain(args)
|
||||
}
|
||||
|
||||
fun stackVmMain(args: Array<String>) {
|
||||
println("\nProg8 StackVM by Irmen de Jong (irmen@razorvine.net)")
|
||||
// @todo decide on software license
|
||||
// println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||
|
@ -398,7 +398,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
if(continueStmtLabelStack.empty())
|
||||
throw CompilerException("continue outside of loop statement block")
|
||||
val label = continueStmtLabelStack.peek()
|
||||
prog.instr(Opcode.JUMP, null, label)
|
||||
prog.instr(Opcode.JUMP, callLabel = label)
|
||||
}
|
||||
|
||||
private fun translate(stmt: Break) {
|
||||
@ -406,7 +406,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
if(breakStmtLabelStack.empty())
|
||||
throw CompilerException("break outside of loop statement block")
|
||||
val label = breakStmtLabelStack.peek()
|
||||
prog.instr(Opcode.JUMP, null, label)
|
||||
prog.instr(Opcode.JUMP, callLabel = label)
|
||||
}
|
||||
|
||||
private fun translate(branch: BranchStatement) {
|
||||
@ -930,7 +930,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun StatementTranslator.translateSwap(args: List<IExpression>) {
|
||||
private fun translateSwap(args: List<IExpression>) {
|
||||
// swap(x,y) is treated differently, it's not a normal function call
|
||||
if (args.size != 2)
|
||||
throw AstException("swap requires 2 arguments")
|
||||
@ -940,65 +940,49 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
throw AstException("swap requires 2 args of identical type")
|
||||
if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null)
|
||||
throw AstException("swap requires 2 variables, not constant value(s)")
|
||||
if (dt1 !in NumericDatatypes)
|
||||
throw AstException("swap requires args of numerical type")
|
||||
if(same(args[0], args[1]))
|
||||
throw AstException("swap should have 2 different args")
|
||||
if(dt1 !in NumericDatatypes)
|
||||
throw AstException("swap requires args of numerical type")
|
||||
// @todo implement these errors as nice AstChecker expression errors.
|
||||
|
||||
// @todo implement the above errors as nice AstChecker expression errors.
|
||||
// @todo implement this more efficiently with using the xor trick instead of the stack!
|
||||
// Swap(X,Y) :=
|
||||
// eor trick: Swap(X,Y) :=
|
||||
// X ^= Y
|
||||
// Y ^= X
|
||||
// X ^= Y
|
||||
// for floats, this doesn't work, use a temp variable instead.
|
||||
// this trick is used when we're dealing with: (u)byte or (u)word variables, ... @todo
|
||||
|
||||
// pop first then second arg
|
||||
translate(args[0])
|
||||
translate(args[1])
|
||||
// pop stack in reverse order
|
||||
when {
|
||||
args[0] is IdentifierReference -> {
|
||||
val target = AssignTarget(null, args[0] as IdentifierReference, null, null, args[0].position)
|
||||
popValueIntoTarget(target, dt1)
|
||||
}
|
||||
args[0] is RegisterExpr -> {
|
||||
val target = AssignTarget((args[0] as RegisterExpr).register, null, null, null, args[0].position)
|
||||
popValueIntoTarget(target, dt1)
|
||||
}
|
||||
args[0] is ArrayIndexedExpression -> {
|
||||
val target = AssignTarget(null, null, args[0] as ArrayIndexedExpression, null, args[0].position)
|
||||
popValueIntoTarget(target, dt1)
|
||||
}
|
||||
args[0] is DirectMemoryRead -> {
|
||||
val target = AssignTarget(null, null, null, DirectMemoryWrite((args[0] as DirectMemoryRead).addressExpression, args[0].position), args[0].position)
|
||||
popValueIntoTarget(target, dt1)
|
||||
}
|
||||
else -> TODO("unpop type ${args[0]}")
|
||||
}
|
||||
|
||||
when {
|
||||
args[1] is IdentifierReference -> {
|
||||
val target = AssignTarget(null, args[1] as IdentifierReference, null, null, args[1].position)
|
||||
popValueIntoTarget(target, dt2)
|
||||
}
|
||||
args[1] is RegisterExpr -> {
|
||||
val target = AssignTarget((args[1] as RegisterExpr).register, null, null, null, args[1].position)
|
||||
popValueIntoTarget(target, dt2)
|
||||
}
|
||||
args[1] is ArrayIndexedExpression -> {
|
||||
val target = AssignTarget(null, null, args[1] as ArrayIndexedExpression, null, args[1].position)
|
||||
popValueIntoTarget(target, dt2)
|
||||
}
|
||||
args[1] is DirectMemoryRead -> {
|
||||
val target = AssignTarget(null, null, null, DirectMemoryWrite((args[1] as DirectMemoryRead).addressExpression, args[1].position), args[1].position)
|
||||
popValueIntoTarget(target, dt2)
|
||||
}
|
||||
else -> TODO("unpop type ${args[1]}")
|
||||
if(useEorTrickForSwap(dt1, args[0], args[1])) {
|
||||
val xEorY = BinaryExpression(args[0], "^", args[1], args[0].position)
|
||||
val yEorX = BinaryExpression(args[1], "^", args[0], args[1].position)
|
||||
val xIsXeorY = Assignment(listOf(AssignTarget.fromExpr(args[0])), null, xEorY, args[0].position)
|
||||
val yIsYeorX = Assignment(listOf(AssignTarget.fromExpr(args[1])), null, yEorX, args[1].position)
|
||||
xIsXeorY.linkParents(args[0].parent)
|
||||
yIsYeorX.linkParents(args[0].parent)
|
||||
translate(xIsXeorY)
|
||||
translate(yIsYeorX)
|
||||
translate(xIsXeorY)
|
||||
} else {
|
||||
translate(args[0])
|
||||
translate(args[1])
|
||||
// pop in reverse order
|
||||
popValueIntoTarget(AssignTarget.fromExpr(args[0]), dt1)
|
||||
popValueIntoTarget(AssignTarget.fromExpr(args[1]), dt2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
private fun useEorTrickForSwap(dt: DataType, expr1: IExpression, expr2: IExpression): Boolean {
|
||||
if(dt in IntegerDatatypes) {
|
||||
if (expr1 is IdentifierReference && expr2 is IdentifierReference)
|
||||
return true
|
||||
if(expr1 is ArrayIndexedExpression && expr2 is ArrayIndexedExpression) {
|
||||
return expr1.arrayspec.x is LiteralValue && expr2.arrayspec.x is LiteralValue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position) {
|
||||
// evaluate the arguments and assign them into the subroutine's argument variables.
|
||||
var restoreX = Register.X in subroutine.asmClobbers
|
||||
@ -1441,7 +1425,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
prog.line(stmt.position)
|
||||
prog.instr(branchOpcode ?: Opcode.JUMP, jumpAddress, jumpLabel)
|
||||
prog.instr(branchOpcode ?: Opcode.JUMP, jumpAddress, callLabel = jumpLabel)
|
||||
}
|
||||
|
||||
private fun translate(stmt: PostIncrDecr) {
|
||||
@ -1544,28 +1528,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
if(stmt.aug_op!=null) {
|
||||
// augmented assignment
|
||||
when {
|
||||
assignTarget.identifier != null -> {
|
||||
val target = assignTarget.identifier.targetStatement(namespace)!!
|
||||
when(target) {
|
||||
is VarDecl -> {
|
||||
val opcode = opcodePushvar(assignTarget.determineDatatype(namespace, heap, stmt)!!)
|
||||
prog.instr(opcode, callLabel = target.scopedname)
|
||||
}
|
||||
else -> throw CompilerException("invalid assignment target type ${target::class}")
|
||||
}
|
||||
}
|
||||
assignTarget.register != null -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = assignTarget.register.toString())
|
||||
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, false)
|
||||
assignTarget.memoryAddress != null -> {
|
||||
TODO("translate aug assign on memory address $stmt")
|
||||
}
|
||||
}
|
||||
|
||||
translateAugAssignOperator(stmt.aug_op, stmt.value.resultingDatatype(namespace, heap))
|
||||
}
|
||||
if(stmt.aug_op!=null)
|
||||
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
||||
|
||||
if(stmt.value is FunctionCall) {
|
||||
val sub = (stmt.value as FunctionCall).target.targetStatement(namespace)
|
||||
@ -1708,82 +1672,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateAugAssignOperator(aug_op: String, valueDt: DataType?) { // @todo: not used in practice? (all augassigns are converted to normal assigns)
|
||||
if(valueDt==null)
|
||||
throw CompilerException("value datatype not known")
|
||||
val validDt = setOf(DataType.UBYTE, DataType.UWORD, DataType.FLOAT)
|
||||
if(valueDt !in validDt)
|
||||
throw CompilerException("invalid datatype(s) for operand(s)")
|
||||
val opcode = when(aug_op) {
|
||||
// @todo ... need more datatypes here?
|
||||
"+=" -> {
|
||||
when (valueDt) {
|
||||
DataType.UBYTE -> Opcode.ADD_UB
|
||||
DataType.UWORD -> Opcode.ADD_UW
|
||||
DataType.FLOAT -> Opcode.ADD_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"-=" -> {
|
||||
when (valueDt) {
|
||||
DataType.UBYTE -> Opcode.SUB_UB
|
||||
DataType.UWORD -> Opcode.SUB_UW
|
||||
DataType.FLOAT -> Opcode.SUB_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"/=" -> {
|
||||
when (valueDt) {
|
||||
DataType.UBYTE -> Opcode.IDIV_UB
|
||||
DataType.BYTE -> Opcode.IDIV_B
|
||||
DataType.UWORD -> Opcode.IDIV_UW
|
||||
DataType.WORD -> Opcode.IDIV_W
|
||||
DataType.FLOAT -> Opcode.DIV_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"*=" -> {
|
||||
when (valueDt) {
|
||||
DataType.UBYTE -> Opcode.MUL_UB
|
||||
DataType.UWORD -> Opcode.MUL_UW
|
||||
DataType.FLOAT -> Opcode.MUL_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"**=" -> {
|
||||
when (valueDt) {
|
||||
DataType.UBYTE -> Opcode.POW_UB
|
||||
DataType.UWORD -> Opcode.POW_UW
|
||||
DataType.FLOAT -> Opcode.POW_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"&=" -> {
|
||||
when(valueDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITAND_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITAND_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"|=" -> {
|
||||
when(valueDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITOR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITOR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"^=" -> {
|
||||
when(valueDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITXOR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITXOR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
else -> throw CompilerException("invalid aug assignment operator $aug_op")
|
||||
}
|
||||
prog.instr(opcode)
|
||||
}
|
||||
|
||||
private fun translate(stmt: Return) {
|
||||
// put the return values on the stack, in reversed order. The caller will process them.
|
||||
for(value in stmt.values.reversed()) {
|
||||
|
@ -345,8 +345,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
}
|
||||
}
|
||||
|
||||
fun instr(opcode: Opcode, arg: Value? = null, callLabel: String? = null) {
|
||||
currentBlock.instructions.add(Instruction(opcode, arg, callLabel = callLabel))
|
||||
fun instr(opcode: Opcode, arg: Value? = null, arg2: Value? = null, callLabel: String? = null, callLabel2: String? = null) {
|
||||
currentBlock.instructions.add(Instruction(opcode, arg, arg2, callLabel, callLabel2))
|
||||
}
|
||||
|
||||
fun label(labelname: String, asmProc: Boolean=false) {
|
||||
|
@ -195,7 +195,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
vardecls2asm(block)
|
||||
out("")
|
||||
|
||||
val instructionPatternWindowSize = 6 // increase once patterns occur longer than this.
|
||||
val instructionPatternWindowSize = 7 // increase once patterns occur longer than this.
|
||||
var processed = 0
|
||||
|
||||
if(trace) println("BLOCK: ${block.scopedname} ${block.address ?: ""}")
|
||||
@ -203,9 +203,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
if(trace) println("\t${ins[0].toString().trim()}")
|
||||
if (processed == 0) {
|
||||
processed = instr2asm(ins)
|
||||
if (processed == 0)
|
||||
// the instructions are not recognised yet and can't be translated into assembly
|
||||
if (processed == 0) {
|
||||
// the instructions are not recognised yet and can't be translated into assembly
|
||||
throw CompilerException("no asm translation found for instruction pattern: $ins")
|
||||
}
|
||||
}
|
||||
processed--
|
||||
}
|
||||
@ -3050,6 +3051,91 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
dex
|
||||
"""
|
||||
},
|
||||
// push var byte & var byte
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITAND_BYTE, Opcode.POP_VAR_BYTE)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
and ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
"""
|
||||
},
|
||||
// push var byte | var byte
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITOR_BYTE, Opcode.POP_VAR_BYTE)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
ora ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
"""
|
||||
},
|
||||
// push var byte ^ var byte
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITXOR_BYTE, Opcode.POP_VAR_BYTE)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
eor ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
"""
|
||||
},
|
||||
// push var word & var word
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITAND_WORD, Opcode.POP_VAR_WORD)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
and ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
lda ${segment[0].callLabel}+1
|
||||
and ${segment[1].callLabel}+1
|
||||
sta ${segment[3].callLabel}+1
|
||||
"""
|
||||
},
|
||||
// push var word | var word
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITOR_WORD, Opcode.POP_VAR_WORD)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
ora ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
lda ${segment[0].callLabel}+1
|
||||
ora ${segment[1].callLabel}+1
|
||||
sta ${segment[3].callLabel}+1
|
||||
"""
|
||||
},
|
||||
// push var word ^ var word
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITXOR_WORD, Opcode.POP_VAR_WORD)) { segment ->
|
||||
"""
|
||||
lda ${segment[0].callLabel}
|
||||
eor ${segment[1].callLabel}
|
||||
sta ${segment[3].callLabel}
|
||||
lda ${segment[0].callLabel}+1
|
||||
eor ${segment[1].callLabel}+1
|
||||
sta ${segment[3].callLabel}+1
|
||||
"""
|
||||
},
|
||||
|
||||
// bytearray[consti3] = bytearray[consti1] ^ bytearray[consti2]
|
||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE,
|
||||
Opcode.BITXOR_BYTE, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment ->
|
||||
val i1 = segment[5].arg!!.integerValue()
|
||||
val i2 = segment[0].arg!!.integerValue()
|
||||
val i3 = segment[2].arg!!.integerValue()
|
||||
"""
|
||||
lda ${segment[1].callLabel}+$i2
|
||||
eor ${segment[3].callLabel}+$i3
|
||||
sta ${segment[6].callLabel}+$i1
|
||||
"""
|
||||
},
|
||||
// warray[consti3] = warray[consti1] ^ warray[consti2]
|
||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD,
|
||||
Opcode.BITXOR_WORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
|
||||
val i1 = segment[5].arg!!.integerValue()*2
|
||||
val i2 = segment[0].arg!!.integerValue()*2
|
||||
val i3 = segment[2].arg!!.integerValue()*2
|
||||
"""
|
||||
lda ${segment[1].callLabel}+$i2
|
||||
eor ${segment[3].callLabel}+$i3
|
||||
sta ${segment[6].callLabel}+$i1
|
||||
lda ${segment[1].callLabel}+${i2+1}
|
||||
eor ${segment[3].callLabel}+${i3+1}
|
||||
sta ${segment[6].callLabel}+${i1+1}
|
||||
"""
|
||||
},
|
||||
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment ->
|
||||
"""
|
||||
|
@ -1,21 +1,30 @@
|
||||
package prog8.compiler.target.c64
|
||||
|
||||
import prog8.compiler.toHex
|
||||
|
||||
fun optimizeAssembly(lines: MutableList<String>): Int {
|
||||
|
||||
var numberOfOptimizations = 0
|
||||
|
||||
var linesByTwo = getLinesBy(lines, 2)
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var removeLines = optimizeIncDec(linesByTwo)
|
||||
var removeLines = optimizeUselessStackByteWrites(linesByFour)
|
||||
if(removeLines.isNotEmpty()) {
|
||||
for (i in removeLines.reversed())
|
||||
lines.removeAt(i)
|
||||
linesByTwo = getLinesBy(lines, 2)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
removeLines = optimizeStoreLoadSame(linesByTwo)
|
||||
removeLines = optimizeIncDec(linesByFour)
|
||||
if(removeLines.isNotEmpty()) {
|
||||
for (i in removeLines.reversed())
|
||||
lines.removeAt(i)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
removeLines = optimizeStoreLoadSame(linesByFour)
|
||||
if(removeLines.isNotEmpty()) {
|
||||
for (i in removeLines.reversed())
|
||||
lines.removeAt(i)
|
||||
@ -33,6 +42,24 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val removeLines = mutableListOf<Int>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta ${ESTACK_LO.toHex()},x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda ${ESTACK_LO.toHex()},x") {
|
||||
removeLines.add(lines[0].index)
|
||||
removeLines.add(lines[1].index)
|
||||
removeLines.add(lines[2].index)
|
||||
removeLines.add(lines[3].index)
|
||||
}
|
||||
}
|
||||
return removeLines
|
||||
}
|
||||
|
||||
fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Int> {
|
||||
|
||||
// optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
||||
@ -102,10 +129,10 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding pairs of 2
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeStoreLoadSame(linesByTwo: List<List<IndexedValue<String>>>): List<Int> {
|
||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
||||
val removeLines = mutableListOf<Int>()
|
||||
for (pair in linesByTwo) {
|
||||
for (pair in linesByFour) {
|
||||
val first = pair[0].value.trimStart()
|
||||
val second = pair[1].value.trimStart()
|
||||
|
||||
|
@ -1,54 +1,71 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() {
|
||||
|
||||
word[8] rotatedx = [11,33,55,77,22,44,66,88]
|
||||
word[8] rotatedy = [11,33,55,77,22,44,66,88]
|
||||
word[8] rotatedz = [1,3,-5,7,2,4,-6,8]
|
||||
ubyte ub1
|
||||
ubyte ub2
|
||||
byte b1
|
||||
byte b2
|
||||
uword uw1
|
||||
uword uw2
|
||||
word w1
|
||||
word w2
|
||||
|
||||
printarray()
|
||||
ubyte[3] uba
|
||||
byte[3] ba
|
||||
uword[3] uwa
|
||||
word[3] wa
|
||||
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
; ub1 = ub2 & 44
|
||||
; b1 = b2 & 44
|
||||
; uw1 = uw2 & 4444
|
||||
; w1 = w2 & 4444
|
||||
; ub1 = ub2 | 44
|
||||
; b1 = b2 | 44
|
||||
; uw1 = uw2 | 4444
|
||||
; w1 = w2 | 4444
|
||||
; ub1 = ub2 ^ 44
|
||||
; b1 = b2 ^ 44
|
||||
; uw1 = uw2 ^ 4444
|
||||
; w1 = w2 ^ 4444
|
||||
;
|
||||
; ub1 = ub2 & ub1
|
||||
; b1 = b2 & b1
|
||||
; uw1 = uw2 & uw1
|
||||
; w1 = w2 & w1
|
||||
; ub1 = ub2 | ub1
|
||||
; b1 = b2 | b1
|
||||
; uw1 = uw2 | uw1
|
||||
; w1 = w2 | w1
|
||||
; ub1 = ub2 ^ ub1
|
||||
; b1 = b2 ^ b1
|
||||
; uw1 = uw2 ^ uw1
|
||||
; w1 = w2 ^ w1
|
||||
|
||||
for ubyte sorti in 6 to 0 step -1 {
|
||||
for ubyte i1 in 0 to sorti {
|
||||
ubyte i2=i1+1
|
||||
if(rotatedz[i2]>rotatedz[i1]) {
|
||||
swap(rotatedx[i1], rotatedx[i2])
|
||||
swap(rotatedy[i1], rotatedy[i2])
|
||||
swap(rotatedz[i1], rotatedz[i2])
|
||||
}
|
||||
}
|
||||
}
|
||||
swap(ub1, ub2)
|
||||
swap(b1, b2)
|
||||
swap(uw1, uw2)
|
||||
swap(w1, w2)
|
||||
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
swap(uba[0], uba[1])
|
||||
swap(ba[0], ba[1])
|
||||
swap(uwa[0], uwa[1])
|
||||
swap(wa[0], wa[1])
|
||||
|
||||
printarray()
|
||||
; this goes without xor trick:
|
||||
ubyte i1
|
||||
ubyte i2
|
||||
swap(uba[i1], uba[i2])
|
||||
swap(ba[i1], ba[i2])
|
||||
swap(uwa[i1], uwa[i2])
|
||||
swap(wa[i1], wa[i2])
|
||||
|
||||
|
||||
sub printarray() {
|
||||
for word a in rotatedx {
|
||||
c64scr.print_w(a)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
for word a in rotatedy {
|
||||
c64scr.print_w(a)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
for word a in rotatedz {
|
||||
c64scr.print_w(a)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
swap(uba[1], ub1)
|
||||
swap(uba[i1], ub1)
|
||||
swap(uwa[1], uw1)
|
||||
swap(uwa[i1], uw1)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
package prog8.parser;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
package prog8.parser;
|
||||
import org.antlr.v4.runtime.atn.*;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
|
Loading…
Reference in New Issue
Block a user