
com.jtransc.ast.feature.gotos.kt Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2016 Carlos Ballesteros Velasco
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jtransc.ast.feature
import com.jtransc.ast.*
import com.jtransc.ast.optimize.optimize
import com.jtransc.graph.Relooper
import com.jtransc.graph.RelooperException
object GotosFeature : AstFeature() {
override fun remove(body: AstBody, settings: AstBuildSettings): AstBody {
if (settings.relooper) {
try {
return removeRelooper(body) ?: removeMachineState(body)
} catch (t: Throwable) {
t.printStackTrace()
return removeMachineState(body)
}
} else {
return removeMachineState(body)
}
}
private fun removeRelooper(body: AstBody): AstBody? {
class BasicBlock(var index: Int) {
var node: Relooper.Node? = null
var visited = false
val stms = arrayListOf()
var next: BasicBlock? = null
var condExpr: AstExpr? = null
var ifNext: BasicBlock? = null
var switchNext: Map? = null
//val targets by lazy { (listOf(next, ifNext) + (switchNext?.values ?: listOf())).filterNotNull() }
override fun toString(): String = "BasicBlock($index)"
}
val entryStm = body.stm
if (entryStm !is AstStm.STMS) return null // Not relooping single statements
if (body.traps.isNotEmpty()) return null // Not relooping functions with traps by the moment
val stms = entryStm.stms
val bblist = arrayListOf()
var bbs = hashMapOf()
fun createBB(): BasicBlock {
val bb = BasicBlock(bblist.size)
bblist += bb
return bb
}
fun getBBForLabel(label: AstLabel): BasicBlock {
return bbs.getOrPut(label) { createBB() }
}
val entry = createBB()
var current = entry
for (stmBox in stms) {
val stm = stmBox.value
when (stm) {
is AstStm.STM_LABEL -> {
val prev = current
current = getBBForLabel(stm.label)
prev.next = current
}
is AstStm.GOTO -> {
val prev = current
current = createBB()
prev.next = getBBForLabel(stm.label)
}
is AstStm.IF_GOTO -> {
val prev = current
current = createBB()
prev.condExpr = stm.cond.value
prev.ifNext = getBBForLabel(stm.label)
prev.next = current
}
is AstStm.SWITCH_GOTO -> {
// Not handled switches yet!
return null
}
is AstStm.RETURN, is AstStm.THROW, is AstStm.RETHROW -> {
current.stms += stm
val prev = current
current = createBB()
prev.next = null
}
else -> {
current.stms += stm
}
}
}
val relooper = Relooper()
for (n in bblist) {
n.node = relooper.node(n.stms)
//println("NODE(${n.index}): ${n.stms}")
//if (n.next != null) println(" -> ${n.next}")
//if (n.ifNext != null) println(" -> ${n.ifNext} [${n.condExpr}]")
}
for (n in bblist) {
val next = n.next
val ifNext = n.ifNext
if (next != null) relooper.edge(n.node!!, next.node!!)
if (n.condExpr != null && ifNext != null) relooper.edge(n.node!!, ifNext.node!!, n.condExpr!!)
}
try {
return AstBody(relooper.render(bblist[0].node!!)?.optimize(body.flags) ?: return null, body.locals, body.traps, body.flags)
} catch (e: RelooperException) {
//println("RelooperException: ${e.message}")
return null
}
//return AstBody(relooper.render(bblist[0].node!!) ?: return null, body.locals, body.traps)
}
fun removeMachineState(body: AstBody): AstBody {
// @TODO: this should create simple blocks and do analysis like that, instead of creating a gigantic switch
// @TODO: trying to generate whiles, ifs and so on to allow javascript be fast. See relooper paper.
var stm = body.stm
var locals = body.locals.toCollection(arrayListOf())
val traps = body.traps.toCollection(arrayListOf())
//val gotostate = AstLocal(-1, "_gotostate", AstType.INT)
val gotostate = AstExpr.LOCAL(AstLocal(-1, "G", AstType.INT))
var hasLabels = false
var stateIndex = 0
val labelsToState = hashMapOf()
fun getStateFromLabel(label: AstLabel): Int {
if (label !in labelsToState) {
labelsToState[label] = ++stateIndex
}
return labelsToState[label]!!
}
fun strip(stm: AstStm): AstStm = when (stm) {
is AstStm.STMS -> {
// Without labels/gotos
if (!stm.stms.any { it.value is AstStm.STM_LABEL }) {
stm
}
// With labels/gotos
else {
hasLabels = true
val stms = stm.stms
var stateIndex = 0
var stateStms = arrayListOf()
val cases = arrayListOf>()
fun flush() {
cases.add(Pair(stateIndex, AstStm.STMS(stateStms)))
stateIndex = -1
stateStms = arrayListOf()
}
fun simulateGotoLabel(index: Int): AstStm = AstStm.STMS(
AstStm.SET_LOCAL(gotostate, AstExpr.LITERAL(index)),
AstStm.CONTINUE()
)
fun simulateGotoLabel(label: AstLabel): AstStm = simulateGotoLabel(getStateFromLabel(label))
for (ss in stms) {
val s = ss.value
when (s) {
is AstStm.STM_LABEL -> {
val nextIndex = getStateFromLabel(s.label)
val lastStm = stateStms.lastOrNull()
if ((lastStm !is AstStm.CONTINUE) && (lastStm !is AstStm.BREAK) && (lastStm !is AstStm.RETURN)) {
stateStms.add(simulateGotoLabel(s.label))
}
flush()
stateIndex = nextIndex
stateStms = arrayListOf()
}
is AstStm.IF_GOTO -> {
stateStms.add(AstStm.IF(
s.cond.value,
simulateGotoLabel(s.label)
))
}
is AstStm.GOTO -> {
stateStms.add(simulateGotoLabel(s.label))
}
is AstStm.SWITCH_GOTO -> {
//throw NotImplementedError("Must implement switch goto ")
stateStms.add(AstStm.SWITCH(
s.subject.value,
simulateGotoLabel(s.default),
s.cases.map {
Pair(it.first, simulateGotoLabel(it.second))
}
))
}
else -> {
stateStms.add(s)
}
}
}
flush()
val plainWhile = AstStm.WHILE(AstExpr.LITERAL(true),
AstStm.SWITCH(gotostate, AstStm.NOP("no default"), cases)
)
if (traps.isEmpty()) {
plainWhile
} else {
// Calculate ranges for try...catch
val checkTraps = traps.map { trap ->
val startState = getStateFromLabel(trap.start)
val endState = getStateFromLabel(trap.end)
val handlerState = getStateFromLabel(trap.handler)
AstStm.IF(
(gotostate ge startState.lit) band (gotostate le endState.lit) band (AstExpr.CAUGHT_EXCEPTION() instanceof trap.exception),
simulateGotoLabel(handlerState)
)
}
AstStm.WHILE(AstExpr.LITERAL(true),
AstStm.TRY_CATCH(plainWhile, AstStm.STMS(
checkTraps.stms,
AstStm.RETHROW()
))
)
}
}
}
else -> stm
}
stm = strip(stm)
if (hasLabels) {
locals.add(gotostate.local)
}
return AstBody(stm, locals, traps, body.flags)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy