diff --git a/codeCore/src/prog8/code/source/ImportFileSystem.kt b/codeCore/src/prog8/code/source/ImportFileSystem.kt index 26ef70c26..c0674271f 100644 --- a/codeCore/src/prog8/code/source/ImportFileSystem.kt +++ b/codeCore/src/prog8/code/source/ImportFileSystem.kt @@ -3,20 +3,16 @@ package prog8.code.source import prog8.code.core.Position import prog8.code.sanitize import java.nio.file.Path -import java.util.* +import java.util.concurrent.ConcurrentHashMap import kotlin.io.path.Path // Resource caching "filesystem". // Note that it leaves the decision to load a resource or an actual disk file to the caller. +// Thread-safe: uses ConcurrentHashMap for all caches. object ImportFileSystem { - fun clearCaches() { - cache.clear() - lineSpanCache.clear() - } - fun expandTilde(path: String): String = if (path.startsWith("~")) { val userHome = System.getProperty("user.home") userHome + path.drop(1) @@ -28,47 +24,40 @@ object ImportFileSystem { fun getFile(path: Path, isLibrary: Boolean=false): SourceCode { val normalized = path.sanitize() - val cached = cache[normalized.toString()] - if (cached != null) - return cached - val file = SourceCode.File(normalized, isLibrary) - cache[normalized.toString()] = file - return file + return cache.computeIfAbsent(normalized.toString().lowercase()) { + SourceCode.File(normalized, isLibrary) + } } fun getResource(name: String): SourceCode { - val cached = cache[name] - if (cached != null) return cached - val resource = SourceCode.Resource(name) - cache[name] = resource - return resource + return cache.computeIfAbsent(name.lowercase()) { + SourceCode.Resource(name) + } } fun retrieveSourceLine(position: Position): String { if(SourceCode.isLibraryResource(position.file)) { val key = SourceCode.withoutPrefix(position.file) - val cached = cache[key] + val cached = cache[key.lowercase()] ?: runCatching { getResource(key) }.getOrNull() if(cached != null) return getLine(cached, position.line) } - val cached = cache[position.file] + val cached = cache[position.file.lowercase()] if(cached != null) return getLine(cached, position.line) val path = Path(position.file).sanitize() - val cached2 = cache[path.toString()] + val cached2 = cache[path.toString().lowercase()] if(cached2 != null) return getLine(cached2, position.line) throw NoSuchElementException("cannot get source line $position, with path $path") } private fun getLine(code: SourceCode, lineIndex: Int): String { - var spans = lineSpanCache[code] - if(spans==null) { + val spans = lineSpanCache.computeIfAbsent(code) { val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first } val ends = lineSpans.drop(1) + code.text.length - spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray() - lineSpanCache[code] = spans + lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray() } val span = spans[lineIndex - 1] return code.text.substring(span.start, span.end).trim() @@ -76,6 +65,6 @@ object ImportFileSystem { private class LineSpan(val start: Int, val end: Int) - private val cache = TreeMap(String.CASE_INSENSITIVE_ORDER) - private val lineSpanCache = mutableMapOf>() -} \ No newline at end of file + private val cache = ConcurrentHashMap() + private val lineSpanCache = ConcurrentHashMap>() +} diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 1afdcc1dd..e65f4b6e7 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1970,7 +1970,7 @@ class IRCodeGen( } } else { // regular asmsub - if(child.children.map { (it as PtInlineAssembly).isIR }.toSet().size>1) + if(child.children.mapTo(mutableSetOf()) { (it as PtInlineAssembly).isIR }.size>1) errors.err("asmsub mixes IR and non-IR assembly code (could be compiler-generated)", child.position) val asmblocks = child.children.map { (it as PtInlineAssembly).assembly.trimEnd() } val assembly = asmblocks.joinToString("\n") diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index d075628ba..a223622cc 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -13,9 +13,17 @@ import kotlin.io.path.inputStream private const val StMemorySlabBlockName = "prog8_slabs" - class IRFileReader { + companion object { + private val varNoInitPattern = Regex("(?.+?)(?\\[.+?\\])? (?.+) zp=(?.+?)\\s?(split=(?.+?))?\\s?(align=(?.+?))?") + private val constantPattern = Regex("(.+?) (.+)=(.*?)") + private val varInitPattern = Regex("(?.+?)(?\\[.+?\\])? (?.+)=(?.*?) zp=(?.+?)\\s?(split=(?.+?))?\\s?(align=(?.+?))?") + private val memMappedPattern = Regex("@(.+?)(\\[.+?\\])? (.+)=(.+)") + private val slabPattern = Regex("(.+) (.+) (.+)") + private val posPatternSingle = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") + } + fun read(irSourceCode: String): IRProgram { StringReader(irSourceCode).use { stream -> val reader = XMLInputFactory.newInstance().createXMLEventReader(stream) @@ -182,11 +190,10 @@ class IRFileReader { return if(text.isBlank()) emptyList() else { - val varPattern = Regex("(?.+?)(?\\[.+?\\])? (?.+) zp=(?.+?)\\s?(split=(?.+?))?\\s?(align=(?.+?))?") val variables = mutableListOf() text.lineSequence().forEach { line -> // example: uword main.start.qq2 zp=DONTCARE - val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid $segmentname $line") + val match = varNoInitPattern.matchEntire(line) ?: throw IRParseException("invalid $segmentname $line") val type = match.groups["type"]!!.value val arrayspec = match.groups["arrayspec"]?.value ?: "" val name = match.groups["name"]!!.value @@ -217,7 +224,6 @@ class IRFileReader { return if(text.isBlank()) emptyList() else { - val constantPattern = Regex("(.+?) (.+)=(.*?)") val constants = mutableListOf() text.lineSequence().forEach { line -> // examples: @@ -250,13 +256,12 @@ class IRFileReader { return if(text.isBlank()) emptyList() else { - val varPattern = Regex("(?.+?)(?\\[.+?\\])? (?.+)=(?.*?) zp=(?.+?)\\s?(split=(?.+?))?\\s?(align=(?.+?))?") val variables = mutableListOf() text.lineSequence().forEach { line -> // examples: // uword main.start.qq2=0 zp=REQUIRE_ZP // ubyte[6] main.start.namestring=105,114,109,101,110,0 - val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLE $line") + val match = varInitPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLE $line") val type = match.groups["type"]!!.value val arrayspec = match.groups["arrayspec"]?.value ?: "" val name = match.groups["name"]!!.value @@ -381,12 +386,11 @@ class IRFileReader { emptyList() else { val memvars = mutableListOf() - val mappedPattern = Regex("@(.+?)(\\[.+?\\])? (.+)=(.+)") text.lineSequence().forEach { line -> // examples: // @uword main.start.mapped=49152 // @ubyte[20] main.start.mappedarray=49408 - val match = mappedPattern.matchEntire(line) ?: throw IRParseException("invalid MEMORYMAPPED $line") + val match = memMappedPattern.matchEntire(line) ?: throw IRParseException("invalid MEMORYMAPPED $line") val (type, arrayspec, name, address) = match.destructured val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toUInt() else null val dt = parseDatatype(type, arraysize!=null) @@ -407,7 +411,6 @@ class IRFileReader { emptyList() else { val slabs = mutableListOf() - val slabPattern = Regex("(.+) (.+) (.+)") text.lineSequence().forEach { line -> // example: "slabname 4096 0" val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line") @@ -699,9 +702,6 @@ class IRFileReader { } } - // Pattern for single-line position: [file: line 1 col 2-5] - private val posPatternSingle = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") - private fun parsePosition(strpos: String): Position { // example: "[library:/prog8lib/virtual/textio.p8: line 5 col 2-4]" posPatternSingle.matchEntire(strpos)?.let { match -> diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 6267c634b..2ddc781fd 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -1171,27 +1171,27 @@ data class IRInstruction( return this.type } - override fun toString(): String { - val result = mutableListOf(opcode.name.lowercase()) + override fun toString(): String = buildString { + append(opcode.name.lowercase()) when(type) { - IRDataType.BYTE -> result.add(".b ") - IRDataType.WORD -> result.add(".w ") - IRDataType.LONG -> result.add(".l ") - IRDataType.FLOAT -> result.add(".f ") - else -> result.add(" ") + IRDataType.BYTE -> append(".b ") + IRDataType.WORD -> append(".w ") + IRDataType.LONG -> append(".l ") + IRDataType.FLOAT -> append(".f ") + else -> append(" ") } - if(this.fcallArgs!=null) { - immediate?.let { result.add(it.toHex()) } // syscall + if(this@IRInstruction.fcallArgs!=null) { + immediate?.let { append(it.toHex()) } // syscall if(labelSymbol!=null) { // regular subroutine call - result.add(labelSymbol) + append(labelSymbol) if(labelSymbolOffset!=null) - result.add("+$labelSymbolOffset") + append("+$labelSymbolOffset") } - address?.let { result.add(address.toHex()) } // romcall - result.add("(") + address?.let { append(address.toHex()) } // romcall + append("(") fcallArgs.arguments.forEach { val location = if(it.address==null) { if(it.name.isBlank()) "" else it.name+"=" @@ -1205,20 +1205,21 @@ data class IRInstruction( } when(it.reg.dt) { - IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,") - IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,") - IRDataType.LONG -> result.add("${location}r${it.reg.registerNum}.l$cpuReg,") - IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,") + IRDataType.BYTE -> append("${location}r${it.reg.registerNum}.b$cpuReg,") + IRDataType.WORD -> append("${location}r${it.reg.registerNum}.w$cpuReg,") + IRDataType.LONG -> append("${location}r${it.reg.registerNum}.l$cpuReg,") + IRDataType.FLOAT -> append("${location}fr${it.reg.registerNum}.f$cpuReg,") } } - if(result.last().endsWith(',')) { - result.add(result.removeLastOrNull()!!.trimEnd(',')) + if(last() == ',') { + setLength(length - 1) } - result.add(")") + append(")") val returns = fcallArgs.returns if(returns.isNotEmpty()) { - result.add(":") - val resultParts = returns.map { returnspec -> + append(":") + returns.forEachIndexed { index, returnspec -> + if (index > 0) append(",") val cpuReg = if (returnspec.cpuRegister == null) "" else { if (returnspec.cpuRegister.registerOrPair != null) returnspec.cpuRegister.registerOrPair.toString() @@ -1227,64 +1228,55 @@ data class IRInstruction( } if (cpuReg.isEmpty()) { when (returnspec.dt) { - IRDataType.BYTE -> "r${returnspec.registerNum}.b" - IRDataType.WORD -> "r${returnspec.registerNum}.w" - IRDataType.LONG -> "r${returnspec.registerNum}.l" - IRDataType.FLOAT -> "fr${returnspec.registerNum}.f" + IRDataType.BYTE -> append("r${returnspec.registerNum}.b") + IRDataType.WORD -> append("r${returnspec.registerNum}.w") + IRDataType.LONG -> append("r${returnspec.registerNum}.l") + IRDataType.FLOAT -> append("fr${returnspec.registerNum}.f") } } else { when (returnspec.dt) { - IRDataType.BYTE -> "r${returnspec.registerNum}.b@" + cpuReg - IRDataType.WORD -> "r${returnspec.registerNum}.w@" + cpuReg - IRDataType.LONG -> "r${returnspec.registerNum}.l@" + cpuReg - IRDataType.FLOAT -> "r${returnspec.registerNum}.f@" + cpuReg + IRDataType.BYTE -> append("r${returnspec.registerNum}.b@$cpuReg") + IRDataType.WORD -> append("r${returnspec.registerNum}.w@$cpuReg") + IRDataType.LONG -> append("r${returnspec.registerNum}.l@$cpuReg") + IRDataType.FLOAT -> append("r${returnspec.registerNum}.f@$cpuReg") } } } - result.add(resultParts.joinToString(",")) } } else { reg1?.let { - result.add("r$it") - result.add(",") + append("r$it,") } reg2?.let { - result.add("r$it") - result.add(",") + append("r$it,") } reg3?.let { - result.add("r$it") - result.add(",") + append("r$it,") } fpReg1?.let { - result.add("fr$it") - result.add(",") + append("fr$it,") } fpReg2?.let { - result.add("fr$it") - result.add(",") + append("fr$it,") } immediate?.let { - result.add("#${it.toHex()}") - result.add(",") + append("#${it.toHex()},") } immediateFp?.let { - result.add("#${it}") - result.add(",") + append("#${it},") } address?.let { - result.add(it.toHex()) - result.add(",") + append(it.toHex()) + append(",") } labelSymbol?.let { - result.add(it) + append(it) if(labelSymbolOffset!=null) - result.add("+$labelSymbolOffset") + append("+$labelSymbolOffset") } } - if(result.last() == ",") - result.removeLastOrNull() - return result.joinToString("").trimEnd() - } + if(last() == ',') + setLength(length - 1) + }.trimEnd() } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 0062ba56d..7d5bf918c 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -428,23 +428,19 @@ class IRProgram(val name: String, else require(chunk===split.subParent!!.chunks[split.chunkIndex]) val totalSize = chunk.instructions.size - val first = chunk.instructions.dropLast(totalSize-split.splitAt-1) - val second = chunk.instructions.drop(split.splitAt+1) - chunk.instructions.clear() - chunk.instructions.addAll(first) + val splitPoint = split.splitAt + 1 val secondChunk = IRCodeChunk(null, chunk.next) - secondChunk.instructions.addAll(second) + // Move instructions from splitPoint onward to secondChunk without copying + secondChunk.instructions.addAll(chunk.instructions.subList(splitPoint, totalSize)) + chunk.instructions.subList(splitPoint, totalSize).clear() require(chunk.instructions.last().opcode in OpcodesThatEndSSAblock) require(chunk.instructions.size + secondChunk.instructions.size == totalSize) if(chunk.instructions.last().opcode !in OpcodesThatBranchUnconditionally) { chunk.next = secondChunk if(split.blockParent!=null) split.blockParent.children.add(split.chunkIndex+1, secondChunk) else split.subParent!!.chunks.add(split.chunkIndex+1, secondChunk) - // println("split chunk ${chunk.label} at ${split.splitAt}: ${chunk.instructions[split.splitAt]} ${totalSize} = ${chunk.instructions.size}+${secondChunk.instructions.size}") } else { - // shouldn't occur , unreachable code in second chunk? chunk.next = null - // println("REMOVED UNREACHABLE CODE") } } } diff --git a/intermediate/src/prog8/intermediate/Utils.kt b/intermediate/src/prog8/intermediate/Utils.kt index 3a0325357..25fe1d9b7 100644 --- a/intermediate/src/prog8/intermediate/Utils.kt +++ b/intermediate/src/prog8/intermediate/Utils.kt @@ -277,6 +277,8 @@ private class ParsedCall( val returns: List ) +private val callPattern = Regex("(?.+?)\\((?.*?)\\)(:(?.+?))?") + private fun parseCall(rest: String): ParsedCall { fun parseRegspec(reg: String): FunctionCallArgs.RegSpec { @@ -323,8 +325,7 @@ private fun parseCall(rest: String): ParsedCall { } } - val pattern = Regex("(?.+?)\\((?.*?)\\)(:(?.+?))?") - val match = pattern.matchEntire(rest.replace(" ","")) ?: throw IRParseException("invalid call spec $rest") + val match = callPattern.matchEntire(rest.replace(" ","")) ?: throw IRParseException("invalid call spec $rest") val target = match.groups["target"]!!.value val args = match.groups["arglist"]!!.value val arguments = parseArgs(args)