in pxtcompiler/emitter/ir.ts [668:880]
resolve() {
let iterargs = (e: Expr, f: (v: Expr) => Expr) => {
if (e.args)
for (let i = 0; i < e.args.length; ++i)
e.args[i] = f(e.args[i])
}
// after this, totalUses holds the negation of the actual usage count
// also the first SharedRef is replaced with SharedDef
let refdef = (e: Expr): Expr => {
switch (e.exprKind) {
case EK.SharedDef: throw U.oops();
case EK.SharedRef:
let arg = e.args[0]
if (!arg.totalUses) {
arg.totalUses = -1
arg.currUses = 0
arg.irCurrUses = 0
let e2 = Expr.clone(e)
e2.exprKind = EK.SharedDef
e2.args[0] = refdef(e2.args[0])
return e2
} else {
arg.totalUses--;
return e
}
}
iterargs(e, refdef)
return e
}
let opt = (e: ir.Expr): ir.Expr => {
if (e.exprKind == EK.SharedRef)
return e;
iterargs(e, opt)
switch (e.exprKind) {
case EK.Sequence:
e.args = e.args.filter((a, i) => {
if (i != e.args.length - 1 && a.isPure()) {
// in the second opt() phase, we already have computed the total usage counts
// if we drop some expressions, these need to be updated
if (a.exprKind == EK.SharedRef && a.args[0].totalUses > 0)
a.args[0].totalUses--
return false
}
return true
})
break;
}
return e
}
let cntuses = (e: Expr): Expr => {
switch (e.exprKind) {
case EK.SharedDef:
let arg = e.args[0]
//console.log(arg)
U.assert(arg.totalUses < 0, "arg.totalUses < 0")
U.assert(arg.currUses === 0, "arg.currUses === 0")
// if there is just one usage, strip the SharedDef
if (arg.totalUses == -1)
return cntuses(arg)
else
// now, we start counting for real
arg.totalUses = 1;
break;
case EK.SharedRef:
U.assert(e.args[0].totalUses > 0, "e.args[0].totalUses > 0")
e.args[0].totalUses++;
return e;
case EK.PointerLiteral:
const pl = e.ptrlabel()
if (pl) {
if (!pl.lblNumUses) pl.lblNumUses = 0
pl.lblNumUses++
}
break
}
iterargs(e, cntuses)
return e
}
let sharedincr = (e: Expr): Expr => {
//console.log("OUTSH", e.toString())
switch (e.exprKind) {
case EK.SharedDef:
iterargs(e, sharedincr)
case EK.SharedRef:
let arg = e.args[0]
U.assert(arg.totalUses > 0, "arg.totalUses > 0")
if (arg.totalUses == 1) {
U.assert(e.exprKind == EK.SharedDef)
return arg
}
arg.irCurrUses++
return e
default:
iterargs(e, sharedincr)
return e
}
}
this.body = this.body.filter(s => {
if (s.expr) {
//console.log("OPT", s.expr.toString())
s.expr = opt(refdef(s.expr))
//console.log("INTO", s.expr.toString())
if (s.stmtKind == ir.SK.Expr && s.expr.isPure())
return false;
}
return true
})
let lbls = U.toDictionary(this.body.filter(s => s.stmtKind == ir.SK.Label), s => s.lblName)
for (let i = 0; i < this.body.length; ++i)
this.body[i].stmtNo = i
for (let s of this.body) {
if (s.expr) {
//console.log("CNT", s.expr.toString())
s.expr = cntuses(s.expr)
}
switch (s.stmtKind) {
case ir.SK.Expr:
break;
case ir.SK.Jmp:
s.lbl = U.lookup(lbls, s.lblName)
if (!s.lbl) oops("missing label: " + s.lblName)
if (!s.lbl.lblNumUses) s.lbl.lblNumUses = 1
else s.lbl.lblNumUses++
break;
case ir.SK.StackEmpty:
case ir.SK.Label:
case ir.SK.Breakpoint:
case ir.SK.Comment:
break;
default: oops();
}
}
let allBrkp: Breakpoint[] = []
let prev: Stmt = null
let canInline = target.debugMode ? false : true
let inlineBody: Expr = null
for (let s of this.body) {
if (s.expr) {
s.expr = opt(sharedincr(s.expr))
}
// mark Jump-to-next-instruction
if (prev && prev.lbl == s &&
prev.stmtKind == ir.SK.Jmp &&
s.stmtKind == ir.SK.Label &&
prev.jmpMode == ir.JmpMode.Always &&
s.lblNumUses == 1) {
s.lblNumUses = lblNumUsesJmpNext
}
prev = s
if (s.stmtKind == ir.SK.Breakpoint) {
allBrkp[s.breakpointInfo.id] = s.breakpointInfo
} else if (canInline) {
if (s.stmtKind == ir.SK.Jmp) {
if (s.expr) {
if (inlineBody) canInline = false
else inlineBody = s.expr
}
} else if (s.stmtKind == ir.SK.StackEmpty) {
// OK
} else if (s.stmtKind == ir.SK.Label) {
if (s.lblNumUses != lblNumUsesJmpNext)
canInline = false
} else {
canInline = false
}
}
}
if (canInline && inlineBody) {
const bodyCost = inlineWeight(inlineBody)
const callCost = 4 * this.args.length + 4 + 2
const inlineBonus = target.isNative ? 4 : 30
if (bodyCost <= callCost + inlineBonus) {
this.inlineBody = inlineBody
//pxt.log("INLINE: " + inlineWeight(inlineBody) + "/" + callCost + " - " + this.toString())
}
}
if (pxt.options.debug)
pxt.debug(this.toString())
let debugSucc = false
if (debugSucc) {
let s = "BRKP: " + this.getName() + ":\n"
for (let i = 0; i < allBrkp.length; ++i) {
let b = allBrkp[i]
if (!b) continue
s += `${b.line + 1}: `
let n = allBrkp[i + 1]
s += "\n"
}
console.log(s)
}
}