Skip to content
This repository has been archived by the owner on Dec 10, 2022. It is now read-only.

Relooper.2018 #291

Open
wants to merge 61 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
1678d02
Added @JTranscRelooper annotation to manual control relooping
soywiz Jan 14, 2018
565962a
Some unfinished work
soywiz Jan 14, 2018
aef4ecf
Merge branch 'master' of github.com:jtransc/jtransc into relooper.2018
soywiz Jan 15, 2018
8092d1c
Not working: Some work on new relooper
soywiz Jan 15, 2018
6e7aba8
Small fix in relooper
soywiz Jan 15, 2018
30c3c28
More work on relooper
soywiz Jan 15, 2018
ad07e44
Some more work on relooper
soywiz Jan 15, 2018
39d89b7
Some more work on relooper
soywiz Jan 15, 2018
ba7e03d
Some Relooper adjustments
soywiz Jan 15, 2018
404e005
Some more work on relooper
soywiz Jan 15, 2018
8ca8e22
Some more work on relooper
soywiz Jan 15, 2018
3ec897c
Some more work on relooper
soywiz Jan 15, 2018
5ff3685
Some more work on relooper
soywiz Jan 15, 2018
ecd691b
Some more work on relooper
soywiz Jan 15, 2018
3f1c637
Some more work on relooper
soywiz Jan 15, 2018
19f8ee1
Some more work on relooper
soywiz Jan 15, 2018
c7ed617
Some more work on relooper
soywiz Jan 16, 2018
1f5e449
Some more work on relooper
soywiz Jan 16, 2018
829393d
Remove old annotations
soywiz Jan 16, 2018
887797b
Output jtransc version with JS for convenience
soywiz Jan 16, 2018
ca3697f
Old comment
soywiz Jan 16, 2018
0f6ece9
Add a flag for generators to specify if they support labels (most of …
soywiz Jan 16, 2018
61d9b81
Optimize DO_WHILE + if continue + break externally
soywiz Jan 16, 2018
f384cd0
Some more work on relooper
soywiz Jan 16, 2018
91b2168
Supported white and for loops
soywiz Jan 16, 2018
f1faab9
Enable relooper in benchmark
soywiz Jan 16, 2018
ce878e2
This PR will start 0.7 branch
soywiz Jan 16, 2018
84a323c
Inline errors
soywiz Jan 16, 2018
009e53d
Added failing test case using relooper + test code generator when deb…
soywiz Jan 16, 2018
dd4dd2e
Remove empty nodes, that were making the strong component contract (t…
soywiz Jan 16, 2018
1679db2
Do not create additional exit node if not required
soywiz Jan 16, 2018
ae6db58
Revert previos: we need end node for this to work properly
soywiz Jan 16, 2018
1e4c067
Fixed some relooper cases
soywiz Jan 16, 2018
90b2fc1
Some loop optimizations
soywiz Jan 16, 2018
fa640a7
Updated version
soywiz Jan 16, 2018
bc1c646
Detects if && chains
soywiz Jan 17, 2018
26b5b0d
Detects if || chains
soywiz Jan 17, 2018
2932f7e
Small fixes
soywiz Jan 17, 2018
2f13dd0
ConcurrentModification fix
soywiz Jan 17, 2018
fea7cf9
Convert recursive functions into normal queues
soywiz Jan 17, 2018
0a1611f
Optimize IF_ELSE with return on both sides
soywiz Jan 17, 2018
d1bf5d4
Optimize some IF_ELSE to act as guard clauses
soywiz Jan 17, 2018
7dd53ef
Kotlin 1.2.20
soywiz Jan 17, 2018
f39d87c
Fixed a problematic bug related to indices not matching in Relooper
soywiz Jan 17, 2018
3bd4e7a
org.gradle.caching=true
soywiz Jan 17, 2018
e7627df
Throw statements can be also guard clauses
soywiz Jan 17, 2018
8e80373
Remove exit node cycle
soywiz Jan 17, 2018
29f92a8
Detect some ifs leading to the exit point (return and throws)
soywiz Jan 17, 2018
7707735
Prepare to support switches in relooper
soywiz Jan 17, 2018
a083765
Some more work on supporting switch on relooper
soywiz Jan 17, 2018
4b04237
First switch implementation with ifs
soywiz Jan 17, 2018
9cbd1bb
Compact else-if chains to reduce indentation
soywiz Jan 17, 2018
b98197f
Disable switch in relooper temporarily
soywiz Jan 17, 2018
2e84278
Some work on fixing switches with a new/better conversor to basic Stm…
soywiz Jan 17, 2018
d1b3f37
switch fixes
soywiz Jan 17, 2018
34521b8
Some switch fixes
soywiz Jan 17, 2018
10a5ed4
Proper switch working (common node finding still requires some work t…
soywiz Jan 17, 2018
4159fcf
Prefer break/continue over return/throw as guard clauses
soywiz Jan 17, 2018
28699bc
Proper generate switch
soywiz Jan 17, 2018
6ef715c
Fix floating point comparisons
soywiz Jan 18, 2018
a8ebb76
Update badges to represent master instead of last build
soywiz Jan 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Some more work on relooper
  • Loading branch information
soywiz committed Jan 15, 2018
commit 3ec897c84d707eccba1a30d686a259500c904058
7 changes: 7 additions & 0 deletions jtransc-core/src/com/jtransc/ast/AstTransformer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ open class AstTransformer {
is AstStm.IF -> transform(stm)
is AstStm.IF_ELSE -> transform(stm)
is AstStm.WHILE -> transform(stm)
is AstStm.DO_WHILE -> transform(stm)
is AstStm.RETURN -> transform(stm)
is AstStm.RETURN_VOID -> transform(stm)
is AstStm.THROW -> transform(stm)
Expand Down Expand Up @@ -203,6 +204,12 @@ open class AstTransformer {
return stm
}

open fun transform(stm: AstStm.DO_WHILE): AstStm {
transform(stm.iter)
transform(stm.cond)
return stm
}

open fun transform(stm: AstStm.RETURN): AstStm {
transform(stm.retval)
return stm
Expand Down
6 changes: 6 additions & 0 deletions jtransc-core/src/com/jtransc/ast/AstVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ open class AstVisitor {
is AstStm.IF -> visit(stm)
is AstStm.IF_ELSE -> visit(stm)
is AstStm.WHILE -> visit(stm)
is AstStm.DO_WHILE -> visit(stm)
is AstStm.RETURN -> visit(stm)
is AstStm.RETURN_VOID -> visit(stm)
is AstStm.THROW -> visit(stm)
Expand Down Expand Up @@ -206,6 +207,11 @@ open class AstVisitor {
visit(stm.iter)
}

open fun visit(stm: AstStm.DO_WHILE) {
visit(stm.iter)
visit(stm.cond)
}

open fun visit(stm: AstStm.RETURN) {
visit(stm.retval)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import com.jtransc.graph.RelooperException
// @TODO: Use AstBuilder to make it more readable
class GotosFeature : AstMethodFeature() {
override fun remove(method: AstMethod, body: AstBody, settings: AstBuildSettings, types: AstTypes): AstBody {
if (method.relooperEnabled ?: settings.relooper) {
//if (method.relooperEnabled ?: settings.relooper) {
if (method.relooperEnabled ?: false) {
try {
return removeRelooper(method, body, settings, types) ?: removeMachineState(body, types)
} catch (t: Throwable) {
Expand Down
9 changes: 9 additions & 0 deletions jtransc-core/src/com/jtransc/gen/common/CommonGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate {

open fun writeClasses(output: SyncVfsFile) {
if (SINGLE_FILE) {
output.removeIfExists()
if (ADD_UTF8_BOM) {
output[outputFileBaseName] = byteArrayOf(0xEF.toByte(), 0xBB.toByte(), 0xBF.toByte()) + genSingleFileClasses(output).toString().toByteArray()
} else {
Expand Down Expand Up @@ -441,6 +442,7 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate {
is AstStm.SWITCH_GOTO -> genStmSwitchGoto(stm)
is AstStm.NOP -> genStmNop(stm)
is AstStm.WHILE -> genStmWhile(stm)
is AstStm.DO_WHILE -> genStmDoWhile(stm)
is AstStm.IF -> genStmIf(stm)
is AstStm.IF_ELSE -> genStmIfElse(stm)
is AstStm.RETURN_VOID -> genStmReturnVoid(stm, last)
Expand Down Expand Up @@ -1346,6 +1348,13 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate {
}
}
}
fun genStmDoWhile(stm: AstStm.DO_WHILE) = indent {
flowBlock(FlowKind.WHILE) {
line("do", after2 = " while (${stm.cond.genExpr()});") {
line(stm.iter.genStm())
}
}
}

open fun genStmIf(stm: AstStm.IF) = indent {
line("if (${stm.cond.genExpr()})") { line(stm.strue.genStm()) }
Expand Down
102 changes: 66 additions & 36 deletions jtransc-core/src/com/jtransc/graph/Relooper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import java.util.*
import kotlin.collections.LinkedHashSet

class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boolean = false) {
class Graph
class Node(val types: AstTypes, val index: Int, val body: List<AstStm>) {
//var next: Node? = null
val srcEdges = arrayListOf<Edge>()
Expand All @@ -17,6 +16,7 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo
val possibleNextNodes: List<Node> get() = dstEdges.map { it.dst }

val next get() = dstEdges.firstOrNull { it.cond == null }?.dst
val nextEdge get() = dstEdges.firstOrNull { it.cond == null }

override fun toString(): String = "L$index: " + dump(types, body.stm()).toString().replace('\n', ' ').trim() + " EDGES: $dstEdges. SRC_EDGES: ${srcEdges.size}"
}
Expand Down Expand Up @@ -63,7 +63,10 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo
for (n in g.nodes) {
trace { "* $n" }
}
return renderComponents(g.tarjanStronglyConnectedComponentsAlgorithm(), entry)
println("Relooping '$name'...")
val result = renderComponents(g.tarjanStronglyConnectedComponentsAlgorithm(), entry)
println("Relooping '$name'...OK")
return result
}

class RenderContext(val graph: Digraph<Node>) {
Expand Down Expand Up @@ -137,11 +140,12 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo

// Loop
if (isMultiNodeLoop || isSingleNodeLoop) {
trace { "$indent- Detected node loop csize=${component.size} : $node" }
trace { "$indent- LOOP csize=${component.size} : $node" }
val outs = component.getExternalOutputsNodes()
val outsNotInContext = outs.filter { it !in ctx.loopStarts && it !in ctx.loopEnds }
//val outsNotInContext = outs.filter { it !in ctx.loopStarts }
if (outsNotInContext.size != 1) {
trace { "ASSERTION FAILED! outsNotInContext.size != 1 (${outsNotInContext.size}) : $node" }
trace { "$indent- ASSERTION FAILED! outsNotInContext.size != 1 (${outsNotInContext.size}) : $node" }
invalidOp("ERROR When Relooping $name (ASSERTION FAILED)")
}

Expand All @@ -150,20 +154,22 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo

val loopName = ctx.allocName()

trace { "$indent:: ${entryNode.index} - ${exitNode.index}" }
trace { "$indent:: ${component}" }
//trace { "$indent:: ${entryNode.index} - ${exitNode.index}" }
//trace { "$indent:: ${component}" }

ctx.loopStarts[entryNode] = loopName
ctx.loopEnds[exitNode] = loopName

out += AstStm.DO_WHILE(
loopName,
if (isSingleNodeLoop) {
trace { "$indent- render single node: renderNoLoops" }
val out2 = arrayListOf<AstStm>()
renderNoLoops(g, out2, node, exitNode, ctx, level)
//Stm(node.body) // @TODO: Here we should add ifs with breaks, and then convert put the condition there if possible
out2.stmsWoNops
} else {
trace { "$indent- render multi node: renderComponents" }
renderComponents(component.split(entryNode, exitNode), entryNode, exitNode, ctx, level = level + 1)
}
,
Expand All @@ -187,8 +193,20 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo

fun renderNoLoops(g: StrongComponentGraph<Node>, out: ArrayList<AstStm>, node: Node, exit: Node?, ctx: RenderContext, level: Int): Node? {
val indent = INDENTS[level]
trace { "$indent- Detected no loop : $node" }
trace { "$indent- renderNoLoops: Detected no loop : $node" }
out += node.body.stmsWoNops

fun getNodeContinueOrBreak(node: Relooper.Node?): AstStm? {
val loopStart = ctx.loopStarts[node]
val loopEnd = ctx.loopEnds[node]
return when {
loopStart != null -> AstStm.CONTINUE(loopStart)
loopEnd != null -> AstStm.BREAK(loopEnd)
else -> null
}

}

when (node.dstEdges.size) {
0, 1 -> {
if (node.dstEdges.size == 0) {
Expand All @@ -199,44 +217,56 @@ class Relooper(val types: AstTypes, val name: String = "unknown", val debug: Boo
if (node.dstEdges.size == 0) return null
}
2 -> {
trace { "$indent- Node IF (and else?)" }
val ifBody = node.next!!
val endOfIf = node.dstEdgesButNext.firstOrNull() ?: invalidOp("Expected conditional!")
val endOfIfNode = endOfIf.dst
val common = ctx.findCommonSuccessorNotRendered(ifBody, endOfIfNode, exit)

// IF
if (common == endOfIfNode) {
out += AstStm.IF(
endOfIf.cond!!.not(),
renderComponents(g, ifBody, endOfIfNode, ctx, level = level + 1)
)
}
// IF+ELSE
else {
out += AstStm.IF_ELSE(
endOfIf.cond!!,
renderComponents(g, endOfIfNode, common, ctx, level = level + 1),
renderComponents(g, ifBody, common, ctx, level = level + 1)
)
}
return common

}
else -> {
//TODO()
}
}

var ifsAdded = 0
for (e in node.dstEdgesButNext) {
val loopStart = ctx.loopStarts[e.dst]
val loopEnd = ctx.loopEnds[e.dst]
when {
loopStart != null -> out += AstStm.IF(e.cond!!, AstStm.CONTINUE(loopStart))
loopEnd != null -> out += AstStm.IF(e.cond!!, AstStm.BREAK(loopEnd))
else -> TODO()
val breakOrContinue = getNodeContinueOrBreak(e.dst) ?: break
out += AstStm.IF(e.cond!!, breakOrContinue)
ifsAdded++
}

if (ifsAdded == node.dstEdgesButNext.size) {
val breakOrContinue = getNodeContinueOrBreak(node.next)
if (breakOrContinue != null) {
out += breakOrContinue
}
return node.next
}

if (node.dstEdges.size != 2) {
TODO()
}

trace { "$indent- Node IF (and else?)" }
val ifBody = node.next!!
val endOfIf = node.dstEdgesButNext.firstOrNull() ?: invalidOp("Expected conditional!")
val endOfIfNode = endOfIf.dst
val common = ctx.findCommonSuccessorNotRendered(ifBody, endOfIfNode, exit)

// IF
if (common == endOfIfNode) {
out += AstStm.IF(
endOfIf.cond!!.not(),
renderComponents(g, ifBody, endOfIfNode, ctx, level = level + 1)
)
}
// IF+ELSE
else {
out += AstStm.IF_ELSE(
endOfIf.cond!!,
renderComponents(g, endOfIfNode, common, ctx, level = level + 1),
renderComponents(g, ifBody, common, ctx, level = level + 1)
)
}
return node.next
return common


}

fun StrongComponent<Node>.isMultiNodeLoop(): Boolean = (size > 1)
Expand Down
2 changes: 2 additions & 0 deletions jtransc-core/test/com/jtransc/graph/RelooperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class RelooperTest {
if (condLoopOutBreak) {
break;
}
break;
} while (true);
D = 1;
if (condLoopOutContinue) {
Expand All @@ -140,6 +141,7 @@ class RelooperTest {
if (condLoopOutBreak) {
break;
}
break;
} while (true);
}
E = 1;
Expand Down
6 changes: 4 additions & 2 deletions jtransc-gen-js/src/com/jtransc/gen/js/JsTarget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class JsGenerator(injector: Injector) : CommonGenerator(injector) {

@Suppress("UNCHECKED_CAST")
override fun writeClasses(output: SyncVfsFile) {
output[outputFileBaseName] = ""
output["$outputFileBaseName.map"] = ""
val concatFilesTrans = copyFiles(output)

val classesIndenter = arrayListOf<Indenter>()
Expand Down Expand Up @@ -228,13 +230,13 @@ class JsGenerator(injector: Injector) : CommonGenerator(injector) {
// Generate source
//println("outputFileBaseName:$outputFileBaseName")
output[outputFileBaseName] = byteArrayOf(0xEF.toByte(), 0xBB.toByte(), 0xBF.toByte()) + source.toByteArray(Charsets.UTF_8)
if (sourceMap != null) output[outputFileBaseName + ".map"] = sourceMap
if (sourceMap != null) output["$outputFileBaseName.map"] = sourceMap

injector.mapInstance(ConfigJavascriptOutput(output[outputFile]))
}

override fun genSICall(it: AstClass): String {
return "$AWAIT " + "${it.name.targetNameForStatic}" + access("SI", static = true, field = false) + "($JC);"
return "$AWAIT ${it.name.targetNameForStatic}" + access("SI", static = true, field = false) + "($JC);"
}

override fun genStmTryCatch(stm: AstStm.TRY_CATCH) = indent {
Expand Down