callgraph fixed scanning asm subroutines, and deletion of unused subs and modules

This commit is contained in:
Irmen de Jong 2019-06-21 01:47:07 +02:00
parent b374af3526
commit 7de7d5234f
9 changed files with 179 additions and 86 deletions

View File

@ -10,6 +10,7 @@ import prog8.optimizing.simplifyExpressions
import prog8.parser.ParsingFailedError
import prog8.parser.importLibraryModule
import prog8.parser.importModule
import prog8.parser.moduleName
import java.io.File
import java.io.PrintStream
import java.lang.Exception
@ -73,7 +74,7 @@ private fun compileMain(args: Array<String>) {
val totalTime = measureTimeMillis {
// import main module and everything it needs
println("Parsing...")
val programAst = Program(filepath.fileName.toString(), mutableListOf())
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
importModule(programAst, filepath)
val compilerOptions = determineCompilationOptions(programAst)
@ -187,17 +188,17 @@ private fun compileMain(args: Array<String>) {
fun determineCompilationOptions(program: Program): CompilationOptions {
val moduleAst = program.modules.first()
val options = moduleAst.statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
val outputType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%output" }
val mainModule = program.modules.first()
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val launcherType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
as? Directive)?.args?.single()?.name?.toUpperCase()
moduleAst.loadAddress = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%address" }
mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" }
as? Directive)?.args?.single()?.int ?: 0
val zpoption: String? = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val floatsEnabled = options.any { it.name == "enable_floats" }
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val zpType: ZeropageType =
if (zpoption == null)
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
@ -208,7 +209,7 @@ fun determineCompilationOptions(program: Program): CompilationOptions {
ZeropageType.KERNALSAFE
// error will be printed by the astchecker
}
val zpReserved = moduleAst.statements
val zpReserved = mainModule.statements
.asSequence()
.filter { it is Directive && it.directive == "%zpreserved" }
.map { (it as Directive).args }

View File

@ -298,6 +298,10 @@ interface IAstProcessor {
process(addressOf.identifier)
return addressOf
}
fun process(inlineAssembly: InlineAssembly): IStatement {
return inlineAssembly
}
}
@ -312,6 +316,8 @@ interface Node {
return findParentNode<Module>(this)!!
}
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
fun definingScope(): INameScope {
val scope = findParentNode<INameScope>(this)
if(scope!=null) {
@ -384,12 +390,12 @@ interface INameScope {
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
is BranchStatement -> {
subscopes[stmt.truepart.name] = stmt.truepart
if(stmt.elsepart.isNotEmpty())
if(stmt.elsepart.containsCodeOrVars())
subscopes[stmt.elsepart.name] = stmt.elsepart
}
is IfStatement -> {
subscopes[stmt.truepart.name] = stmt.truepart
if(stmt.elsepart.isNotEmpty())
if(stmt.elsepart.containsCodeOrVars())
subscopes[stmt.elsepart.name] = stmt.elsepart
}
}
@ -445,8 +451,8 @@ interface INameScope {
}
}
fun isEmpty() = statements.isEmpty()
fun isNotEmpty() = statements.isNotEmpty()
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
fun containsNoCodeNorVars() = !containsCodeOrVars()
fun remove(stmt: IStatement) {
val removed = statements.remove(stmt)
@ -486,6 +492,17 @@ class Program(val name: String, val modules: MutableList<Module>) {
val loadAddress: Int
get() = modules.first().loadAddress
fun entrypoint(): Subroutine? {
val mainBlocks = modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
if(mainBlocks.size > 1)
throw FatalAstException("more than one 'main' block")
return if(mainBlocks.isEmpty()) {
null
} else {
mainBlocks[0].subScopes()["start"] as Subroutine?
}
}
}
@ -617,19 +634,6 @@ open class Return(var values: List<IExpression>, override val position: Position
override fun toString(): String {
return "Return(values: $values, pos=$position)"
}
fun definingSubroutine(): Subroutine? {
var scope = definingScope()
while(scope !is GlobalNamespace) {
if(scope is Subroutine)
return scope
val parent = scope.parent
if(parent is Subroutine)
return parent
scope = parent.definingScope()
}
return null
}
}
@ -1639,7 +1643,7 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS
this.parent = parent
}
override fun process(processor: IAstProcessor) = this
override fun process(processor: IAstProcessor) = processor.process(this)
}
@ -1692,7 +1696,7 @@ class Subroutine(override val name: String,
override var statements: MutableList<IStatement>,
override val position: Position) : IStatement, INameScope {
override lateinit var parent: Node
val calledBy = mutableSetOf<Subroutine>()
val calledBy = mutableSetOf<INameScope>()
val calls = mutableSetOf<Subroutine>()
val scopedname: String by lazy { makeScopedName(name) }

View File

@ -148,7 +148,7 @@ private class AstChecker(private val program: Program,
}
override fun process(forLoop: ForLoop): IStatement {
if(forLoop.body.isEmpty())
if(forLoop.body.containsNoCodeNorVars())
printWarning("for loop body is empty", forLoop.position)
if(!forLoop.iterable.isIterable(program)) {

View File

@ -170,7 +170,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
} else if(forLoop.loopVar!=null) {
val varName = forLoop.loopVar.nameInSource.last()
if(forLoop.decltype!=null) {
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
if(existing==null) {
// create the local scoped for loop variable itself
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, true, null, false, varName, null, forLoop.loopVar.position)
@ -182,7 +182,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
}
if(forLoop.iterable !is RangeExpr) {
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
if(existing==null) {
// create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, false, ForLoop.iteratorLoopcounterVarname, null, forLoop.loopVar.position)

View File

@ -191,7 +191,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
return r
} else {
// asmsub
if(subroutine.isNotEmpty())
if(subroutine.containsCodeOrVars())
throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine")
prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case
@ -426,7 +426,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
* if the branch statement just contains jumps, more efficient code is generated.
* (just the appropriate branching instruction is outputted!)
*/
if(branch.elsepart.isEmpty() && branch.truepart.isEmpty())
if(branch.elsepart.containsNoCodeNorVars() && branch.truepart.containsNoCodeNorVars())
return
fun branchOpcode(branch: BranchStatement, complement: Boolean) =
@ -470,7 +470,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
val labelElse = makeLabel(branch, "else")
val labelEnd = makeLabel(branch, "end")
val opcode = branchOpcode(branch, true)
if (branch.elsepart.isEmpty()) {
if (branch.elsepart.containsNoCodeNorVars()) {
prog.instr(opcode, callLabel = labelEnd)
translate(branch.truepart)
prog.label(labelEnd)
@ -535,7 +535,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
}
val labelEnd = makeLabel(stmt, "end")
if(stmt.elsepart.isEmpty()) {
if(stmt.elsepart.containsNoCodeNorVars()) {
prog.instr(conditionJumpOpcode, callLabel = labelEnd)
translate(stmt.truepart)
prog.label(labelEnd)
@ -1620,7 +1620,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
}
private fun translate(loop: ForLoop) {
if(loop.body.isEmpty()) return
if(loop.body.containsNoCodeNorVars()) return
prog.line(loop.position)
val loopVarName: String
val loopVarDt: DataType
@ -2154,18 +2154,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
val scopeprefix = if(args[1].str!!.isNotBlank()) "${args[1].str}\t.proc\n" else ""
val scopeprefixEnd = if(args[1].str!!.isNotBlank()) "\t.pend\n" else ""
val filename=args[0].str!!
val sourcecode =
if(filename.startsWith("library:")) {
val resource = tryGetEmbeddedResource(filename.substring(8)) ?: throw IllegalArgumentException("library file '$filename' not found")
resource.bufferedReader().use { it.readText() }
} else {
// first try in the same folder as where the containing file was imported from
val sib = source.resolveSibling(filename)
if(sib.toFile().isFile)
sib.toFile().readText()
else
File(filename).readText()
}
val sourcecode = loadAsmIncludeFile(filename, source)
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=null, callLabel2=scopeprefix+sourcecode+scopeprefixEnd)
}
@ -2179,3 +2168,19 @@ internal class Compiler(private val program: Program): IAstProcessor {
}
}
fun loadAsmIncludeFile(filename: String, source: Path): String {
return if (filename.startsWith("library:")) {
val resource = tryGetEmbeddedResource(filename.substring(8))
?: throw IllegalArgumentException("library file '$filename' not found")
resource.bufferedReader().use { it.readText() }
} else {
// first try in the same folder as where the containing file was imported from
val sib = source.resolveSibling(filename)
if (sib.toFile().isFile)
sib.toFile().readText()
else
File(filename).readText()
}
}

View File

@ -1,16 +1,17 @@
package prog8.optimizing
import prog8.ast.*
import prog8.compiler.loadAsmIncludeFile
class CallGraphBuilder(private val program: Program): IAstProcessor {
private val modulesImporting = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() }
private val modulesImportedBy = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() }
private val subroutinesCalling = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { mutableSetOf() }
private val subroutinesCalledBy = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { mutableSetOf() }
private val subroutinesCalling = mutableMapOf<INameScope, Set<Subroutine>>().withDefault { mutableSetOf() }
private val subroutinesCalledBy = mutableMapOf<Subroutine, Set<INameScope>>().withDefault { mutableSetOf() }
private fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
fun findSubs(scope: INameScope) {
scope.statements.forEach {
if(it is Subroutine)
@ -32,9 +33,6 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
it.importedBy.addAll(modulesImportedBy.getValue(it))
it.imports.addAll(modulesImporting.getValue(it))
if(it.isLibraryModule && it.importedBy.isEmpty())
it.importedBy.add(it) // don't discard auto-imported library module
forAllSubroutines(it) { sub ->
sub.calledBy.clear()
sub.calls.clear()
@ -42,6 +40,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
sub.calledBy.addAll(subroutinesCalledBy.getValue(sub))
sub.calls.addAll(subroutinesCalling.getValue(sub))
}
}
val rootmodule = program.modules.first()
@ -49,21 +48,27 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
}
override fun process(directive: Directive): IStatement {
val thisModule = directive.definingModule()
if(directive.directive=="%import") {
val importedModule: Module = program.modules.single { it.name==directive.args[0].name }
val thisModule = directive.definingModule()
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
} else if (directive.directive=="%asminclude") {
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
val scope = directive.definingScope()
scanAssemblyCode(asm, directive, scope)
}
return super.process(directive)
}
override fun process(functionCall: FunctionCall): IExpression {
val otherSub = functionCall.target.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = functionCall.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
functionCall.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
}
return super.process(functionCall)
}
@ -71,9 +76,10 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = functionCallStatement.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
functionCallStatement.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
}
return super.process(functionCallStatement)
}
@ -81,10 +87,55 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
override fun process(jump: Jump): IStatement {
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = jump.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
jump.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
}
return super.process(jump)
}
override fun process(inlineAssembly: InlineAssembly): IStatement {
// parse inline asm for subroutine calls (jmp, jsr)
val scope = inlineAssembly.definingScope()
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
return super.process(inlineAssembly)
}
private fun scanAssemblyCode(asm: String, context: Node, scope: INameScope) {
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
asm.lines().forEach { line ->
val matches = asmJumpRx.matchEntire(line)
if (matches != null) {
val jumptarget = matches.groups[2]?.value
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
val node = program.namespace.lookup(jumptarget.split('.'), context)
if (node is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(scope)
} else if(jumptarget.contains('.')) {
// maybe only the first part already refers to a subroutine
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
if (node2 is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2)
subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(scope)
}
}
}
} else {
val matches2 = asmRefRx.matchEntire(line)
if (matches2 != null) {
val target= matches2.groups[2]?.value
if (target != null && (target[0].isLetter() || target[0] == '_')) {
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
if (node is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(scope)
}
}
}
}
}
}
}

View File

@ -7,18 +7,18 @@ import kotlin.math.floor
/*
TODO FIX THE OPTIMIZER: RESULTS IN WRONG CODE FOR THE primes.p8 EXAMPLE
todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers)
todo: implement usage counters for variables (locals and heap), blocks. Remove if count is zero.
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
todo remove unused: subroutines, blocks (in this order)
todo remove unused: variable declarations
todo remove unused strings and arrays from the heap
todo inline subroutines that are called exactly once (regardless of their size)
todo inline subroutines that are only called a few times (max 3?)
todo inline subroutines that are "sufficiently small" (0-3 statements)
todo inline subroutines that are called exactly once (regardless of their size)
todo inline subroutines that are only called a few times (max 3?) (if < 20 statements)
todo inline all subroutines that are "very small" (0-3 statements)
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
*/
class StatementOptimizer(private val program: Program) : IAstProcessor {
@ -33,22 +33,50 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
val callgraph = CallGraphBuilder(program)
callgraph.process(program)
// TODO remove unused variables (local and global)
// remove all subroutines that aren't called, or are empty
val removeSubroutines = mutableSetOf<Subroutine>()
val entrypoint = program.entrypoint()
program.modules.forEach {
callgraph.forAllSubroutines(it) { sub ->
if(sub !== entrypoint && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
removeSubroutines.add(sub)
}
}
if(removeSubroutines.isNotEmpty()) {
removeSubroutines.forEach { it.definingScope().statements.remove(it) }
}
val removeBlocks = mutableSetOf<Block>()
// TODO remove blocks that have no incoming references
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
if (block.containsNoCodeNorVars())
removeBlocks.add(block)
}
if(removeBlocks.isNotEmpty()) {
removeBlocks.forEach { it.definingScope().statements.remove(it) }
}
// remove modules that are not imported, or are empty
val removeModules = mutableSetOf<Module>()
program.modules.forEach {
if(it.importedBy.isEmpty() || it.isEmpty()) {
printWarning("discarding empty or unused module: ${it.name}")
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
removeModules.add(it)
}
}
program.modules.removeAll(removeModules)
if(removeModules.isNotEmpty()) {
println("[debug] removing ${removeModules.size} empty/unused modules")
program.modules.removeAll(removeModules)
}
super.process(program)
}
override fun process(block: Block): IStatement {
if(block.statements.isEmpty()) {
// remove empty block
if(block.containsNoCodeNorVars()) {
optimizationsDone++
statementsToRemove.add(block)
}
@ -56,12 +84,10 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
}
override fun process(subroutine: Subroutine): IStatement {
println("STMT OPTIMIZE $subroutine ${subroutine.calledBy} ${subroutine.calls}")
super.process(subroutine)
if(subroutine.asmAddress==null) {
if(subroutine.statements.isEmpty()) {
// remove empty subroutine
if(subroutine.containsNoCodeNorVars()) {
optimizationsDone++
statementsToRemove.add(subroutine)
}
@ -212,13 +238,13 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
override fun process(ifStatement: IfStatement): IStatement {
super.process(ifStatement)
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty()) {
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
statementsToRemove.add(ifStatement)
optimizationsDone++
return ifStatement
}
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) {
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
// invert the condition and move else part to true part
ifStatement.truepart = ifStatement.elsepart
ifStatement.elsepart = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
@ -246,7 +272,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
override fun process(forLoop: ForLoop): IStatement {
super.process(forLoop)
if(forLoop.body.isEmpty()) {
if(forLoop.body.containsNoCodeNorVars()) {
// remove empty for loop
statementsToRemove.add(forLoop)
optimizationsDone++

View File

@ -22,8 +22,11 @@ private class LexerErrorListener: BaseErrorListener() {
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
fun importModule(program: Program, filePath: Path): Module {
print("importing '${filePath.fileName}'")
print("importing '${moduleName(filePath.fileName)}'")
if(filePath.parent!=null) {
var importloc = filePath.toString()
val curdir = Paths.get("").toAbsolutePath().toString()
@ -48,7 +51,7 @@ fun importLibraryModule(program: Program, name: String): Module? {
}
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
val moduleName = modulePath.fileName
val moduleName = moduleName(modulePath.fileName)
val lexer = CustomLexer(modulePath, stream)
val lexerErrors = LexerErrorListener()
lexer.addErrorListener(lexerErrors)

View File

@ -3,6 +3,9 @@
~ main {
; TODO FIX THE COMPILER OPTIMIZER ; RESULTS IN WRONG CODE FOR THIS PROGRAM
ubyte[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop