`

Kilim源码分析之四 ---- 织入之内联subroutine

阅读更多

        小于1.5编译级别时,如果不显示inline try/catch/finally块,try/catch和any会产生的jsr指令跳转到finally。此处分析kilim如何对这种情况下的jsr指令进行内联:finally块中有pausable则会被拷贝一份;finally块中有pausable则会把jsr/ret指令都替换为goto指令,但是并没有像1.5及以后编译级别那样,拷贝一份代码try/catch代码块编译后的指令块中。下面看下analyze中调用的kilim.analysis.MethodFlow.inlineSubroutines()的实现:

   private void inlineSubroutines() throws KilimException {
        markPausableJSRs();//处理finally块,打标记
        while (true) {
            ArrayList<BasicBlock> newBBs = null;
            for (BasicBlock bb: basicBlocks) {
                if (bb.hasFlag(INLINE_CHECKED)) continue;
                bb.setFlag(INLINE_CHECKED);//所有块都是INLINE_CHECKED
                if (bb.lastInstruction() == JSR) {//bb是try/catch代码块的最后一个bb,才inline
                    newBBs = bb.inline();//bb内联
                    if (newBBs != null) {
                        break;
                    }
                }
            }
            if (newBBs == null) { 
                break;
            }
            int id = basicBlocks.size();
            for (BasicBlock bb: newBBs) {
                bb.setId(id++);
                basicBlocks.add(bb);
            }
        }
        // If there are any pausable subroutines, modify the JSRs/RETs to
        // GOTOs
        for (BasicBlock bb: basicBlocks) {
            bb.changeJSR_RET_toGOTOs();//把出现pausable调用的finally块相关的jsr/ret指令换成goto指令来实现
        }
        
    }
    
    private void markPausableJSRs() throws KilimException {
        for (BasicBlock bb: basicBlocks) {
            bb.checkPausableJSR();//这里调用了BasicBlock.checkPausableJSR()
        }
    }
    
    /*
     * If any BB belonging to a subroutine makes a pausable
     * block, it taints all the blocks within the subroutine's
     * purview as PAUSABLE_SUB. 如果subroutine有一个bb是PAUSABLE,那么其所有bbs都是PAUSABLE_SUB的,何用?
     */
    void checkPausableJSR() throws KilimException {
        BasicBlock sub = getJSRTarget();//如果当前bb的最后一条指令是jsr指令,那么第一个后继结点就是jsr Target,jsr指令所在块也只有一个后继结点,即finally代码块的第一个bb。第一次运行到这里时,如果当前分析的方法没有返回值,则当前块是type为any的异常handler,jvm编译期生成的,following bb里边会把异常抛出;如果方法有返回值,当前块保存了返回值在局部变量表,following bb会return局部变量表内容。sub即为finally块内容
        boolean isPausableJSR = false;
        if (sub != null) {
            ArrayList<BasicBlock> subBlocks = sub.getSubBlocks();//finally代码块对应的basicBlocks,如果finally代码块中还有try/catch/finally块,会在jsr指令处终止的,即subBlocks不会包含子finally代码块对应的bb
            for (BasicBlock b: subBlocks) {
                if (b.hasFlag(PAUSABLE)) {//initialize的时候,pausable方法所在bb是PAUSABLE的
                    isPausableJSR = true;
                    break;
                }
            }
            if (isPausableJSR) {
                for (BasicBlock b: subBlocks) {//finally块中有一个是PAUSABLE,整个finally所有subBlocks都是PAUSABLE_SUB
                    b.setFlag(PAUSABLE_SUB);
                }
            }
        }
    }


    /*
     * Invoked on the subroutine entry point's BB. Returns all the BBs
     * linked to it.
     */
    public ArrayList<BasicBlock> getSubBlocks() throws KilimException {
        if (subBlocks == null) {
            if (!hasFlag(IS_SUBROUTINE))//还记得在BasicBlock.initialize的时候我们把jsr指令的目标地址开始的块设置为IS_SUBROUTINE的
                return null;
            subBlocks = new ArrayList<BasicBlock>(10);
            Stack<BasicBlock> stack = new Stack<BasicBlock>();
            this.setFlag(SUB_BLOCK);//IS_SUBROUTINE 也就是 SUB_BLOCK
            stack.add(this);
            while (!stack.isEmpty()) {//如果finally语句块的那一堆代码又被划分成了很多块,那么所有块都是SUB_BLOCK
                BasicBlock b = stack.pop();
                subBlocks.add(b);
                if (b.lastInstruction() == JSR) {//当前块是jsrTarget块,即finally代码块的第一个bb。而在initialize的时候,我们并没有处理过ret指令的目标地址,没有处理过jsr指令的物理following指令,即jsr的物理following指令跟jsr指令是没有关联起来的,所以这里需要关联起来。
                    // add the following block, but not its target
                    BasicBlock follower = b.getFollowingBlock();//获取bb.endPos+1开始的块,一般来说,jsr块的物理following块通常是load、athrow、return等,这些是jsr的目标块运行完需要ret回来的地址。
                    if (!follower.hasFlag(SUB_BLOCK)) {//都标记为SUB_BLOCK
                        follower.setFlag(SUB_BLOCK);
                        stack.push(follower);
                    }
                    continue;//意味着我们跳过了jsr指令的successors,即finally块中的finally块是被跳过了
                }
                for (BasicBlock succ : b.successors) {//jsr指令结尾的块也是有successor的,即它的target块。finally块的结尾是有ret指令的,这个指令所在块是没有后继节点的,subBlocks也就到此位置了。finally块中的try/catch/finally块也会被分析
                    if (succ == this) {
                        thrownew KilimException("JSRs looping back to themselves are not supported");
                    }
                    if (!succ.hasFlag(SUB_BLOCK)) {
                        succ.setFlag(SUB_BLOCK);
                        stack.push(succ);
                    }
                }
            }
            Collections.sort(subBlocks);//按照指令块的物理顺序排序,在consolidate 块的时候按顺序设置了bb的id的,这个id在bb.compareTo中有用到
        }
        return subBlocks;
    }
    /**
     * This basic block's last instruction is JSR. This method initiates a
     * subgraph traversal to identify the called subroutine's boundaries and to
     * make all encountered RET instructions point back to this BB's follower,
     * in essence turning it to a goto. The reason for not actually turning it
     * into a GOTO is that if we don't find any pausable methods in a
     * subroutine, then during code generation we'll simply use the original
     * code. The duplication is still required for flow analysis.
     * 
     * The VM spec is fuzzy on what constitutes the boundaries of a subroutine.
     * We consider the following situations invalid, even though the verifier is
     * ok with it: (a) looping back to itself (b) encountering xRETURN in a subroutine
     * 
     * inline() traverses the graph creating copies of BasicBlocks and labels
     * and keeps a mapping between the old and the new. In the second round, it
     * copies instructions translating any that have labels (branch and switch
     * instructions).
     * 
     * @return mapping of orig basic blocks to new.
     * 
     */
    ArrayList<BasicBlock> inline() throws KilimException {
        HashMap<BasicBlock, BasicBlock> bbCopyMap = null;
        HashMap<Label, Label> labelCopyMap = null;
        BasicBlock targetBB = successors.get(0);//jsr目标块
        Label returnToLabel = flow.getOrCreateLabelAtPos(endPos+1);//jsr指令下边的指令,如果jsr在try块中,就是return;如果jsr是在type为any的异常处理块中,就是aload_X athrow;
        BasicBlock returnToBB = flow.getOrCreateBasicBlock(returnToLabel);
        boolean isPausableSub = targetBB.hasFlag(PAUSABLE_SUB);
        if (!targetBB.hasFlag(SUBROUTINE_CLAIMED)) {
            // This JSR call gets to claim the subroutine's blocks, so no
            // copying required. If another JSR wants to point to the same
            // subroutine, it'll copy BBs on demand)
            targetBB.setFlag(SUBROUTINE_CLAIMED);
            // Tell the RET blocks about the returnTo address and we are done.
            for (BasicBlock b : targetBB.getSubBlocks()) {
                if (b.lastInstruction() == RET) {
                    assert b.successors.size() == 0 : this.toString();
                    b.addSuccessor(returnToBB);//把jsr的下一条指令开始块作为ret指令结束的块的后继结点,即把ret指令和jsr指令关联起来
                }
            }
            return null;
        }
        bbCopyMap = new HashMap<BasicBlock, BasicBlock>(10);
        labelCopyMap = new HashMap<Label, Label>(10);
        successors.clear();
        // first pass
        targetBB.dupBBAndLabels(isPausableSub, bbCopyMap, labelCopyMap, returnToBB);
        addSuccessor(bbCopyMap.get(targetBB));
        // second pass
        return dupCopyContents(isPausableSub, targetBB, returnToBB, bbCopyMap, labelCopyMap);
    }
    //把jsr指令改造成goto指令
    void changeJSR_RET_toGOTOs() throws KilimException {
        int lastInsn = getInstruction(endPos).getOpcode(); 
        if (lastInsn == JSR) {//块的最后一条指令为jsr指令
            BasicBlock targetBB = successors.get(0);
            if (!targetBB.hasFlag(PAUSABLE_SUB)) return;//不是pausable就不处理,因为不会织入,不做改变的。在checkPausableJSR的时候,PAUSABLE bb的所有subBB都是PAUSABLE_SUB的
            changeLastInsnToGOTO(targetBB.startLabel);//把当前块最后一个指令替换成goto,goto的目标是targetBB。其实这里就是替换jsr为goto
            successors.clear();//clear一次有什么用?
            successors.add(targetBB);


            // change the first ASTORE instruction in targetBB to a NOP
            assert targetBB.getInstruction(targetBB.startPos).getOpcode() == ASTORE;//targetBB的第一条指令一定是astroe,存的是jsr后边的指令地址
            targetBB.setInstruction(targetBB.startPos, new NopInsn());//把楼上这个存储jsr下一条指令的地址的指令替换成空指令。这个地址原本会被ret指令用到
            targetBB.unsetFlag(IS_SUBROUTINE);
        } else if (lastInsn == RET && hasFlag(PAUSABLE_SUB)) {
            changeLastInsnToGOTO(successors.get(0).startLabel);//处理jsr targetBB里边的ret指令,改为goto,目标指令是ret的目标块,即jsr的下一条指令所在块。在initialize的时候,ret指令结尾的块是没有successor的,这个在inline的时候关联起来的。
        }//这么整个下来,把jsr、ret指令给替换成了goto,把jsr目标块第一条指令给删掉了。
    }
分享到:
评论

相关推荐

    actor线程库:kilim0.6

    在kilim-0.6这个压缩包中,包含了kilim库的源码、文档和其他相关资源。使用者可以通过阅读源码了解其内部实现机制,或者直接将其导入到项目中使用。kilim的核心特性包括: 1. **异步消息传递**:每个Actor都有自己...

    协程Coroutine和Kilim

    协程Coroutine和Kilim是两个与并发编程相关的概念,主要在Java开发环境中被讨论。在现代软件系统中,多线程和并发处理是提升性能和响应能力的关键技术。协程提供了一种轻量级的并发模型,它比传统的线程更高效,内存...

    基于角色的消息传递框架Kilim.zip

    Kilim 是一种 Java 消息传递框架,提供了超轻量级的线程,推动了线程之间迅速、安全、无需复制的消息传递的实现。 Kilim 使用 Java 编写,融入了角色模型的概念。在 Kilim 中,“角色” 是使用 Kilim 的 Task 类型...

    JAVA版本his系统源码-kilim:用于Java的轻量级线程,具有消息传递、nio、http和调度支持

    JAVA版本他的系统源码Kilim :JVM 的延续、纤维、Actor 和消息传递 Kilim 由 2 个主要组件组成: Kilim weaver 修改已编译的 java 类的字节码,启用一种方法来保存它的状态并放弃对其线程的控制,即协同多任务 Kilim...

    kilim-motifs:具有Lindenmayer系统的程序性Kilim主题生成

    《kilim-motifs:基于Lindenmayer系统的程序化Kilim图案生成》 ...同时,"kilim-motifs"也提供了一个实例,说明了开源软件如何促进创新,让全球的开发者都能参与到艺术和技术的融合之中,推动生成艺术的发展。

    vivo X9主板原理图及PCB板图

    通过对该设备的主板原理图及 PCB 板图进行分析,可以更好地理解其内部结构与工作原理,从而在维修过程中提高效率并确保维修质量。 #### 主板原理图解析 1. **主板概述**:vivo X9 的主板主要包含了电源管理、数据...

    基于Kilim、Promise JDeferred、Zookeeper和Spring Boot的协程分布式调用聚合框架设计源码

    该项目是一款基于Kilim、Promise JDeferred、Zookeeper和Spring Boot技术的协程驱动分布式调用聚合框架。源码包含223个文件,涵盖143个Java源文件、33个XML配置文件、13个GIF图片、7个JAR包、5个批处理脚本、4个属性...

    mapleliang-Jungle Server Core使用与扩展1

    Jungle Server Core采用Maven进行构建,典型的项目源码结构如下: - `svr`:启动类位于`com.tencent.svrcore.demo.Bootstrap`,负责初始化依赖管理容器Guice,加载配置文件`jungle.properties`,定义传输层协议...

    协程式驱动框架Nepxion-Coroutine.zip

    Coroutine是基于Kilim/Promise JDeferred的协程式驱动框架,基于Apache Zookeeper的分布式规则存储和动态规则变更通知。 主要特性: 1. 基于微服务框架理念设计 2. 支持同步/异步调用 3. 支持串行/并行调用 4....

    在Java中使用协程.pdf

    在Java中,虽然语言本身并不直接支持协程,但在Sun JDK 7发布之前,开发者可以借助其他工具或语言,如Scala和Kilim,来实现协程的概念。 Scala是一种基于JVM的多范式编程语言,它不仅支持面向对象编程,还支持函数...

Global site tag (gtag.js) - Google Analytics