resolve()

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)
            }
        }