`
NeuronR
  • 浏览: 58964 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

[优化]删去 NOP 指令

阅读更多

在控制语句的指令生成过程中, 语句无法知道外界指令结构, 不能贸然将跳出语句定向到外部指令, 因此使用了 NOP 指令缓冲了这些跳转. 然而, 在全部指令生成结束后, 跳转指令就可以根据上下文选择正确的目标了, 那么 NOP 指令只是影响性能的冗余指令了. 本着兔死狗烹的原则, 现在去掉这些指令.

static void cleanNop(struct List* instructions);

具体的做法是, 为每个 NOP 指令记录它之后的第一条非 NOP 指令, 然后扫描指令序列, 重定向它们的目标, 然后删去全部 NOP 指令. 现在先给指令数据结构增加一个索引域

/* 增加 index 域 */
#define memberAbstractInstruction \
    int segOffset;                \
    int index;                    \
    InstructionCode code; /*******/
struct AbstractInstruction {
    memberAbstractInstruction
};

对于非 NOP 指令, 它的索引域等于它自己的位置索引, 而 NOP 指令的索引域等于该指令之后第一条非 NOP 指令的索引.

对整个指令序列进行一次逆向遍历, 计算它们的索引值

    int index = instructions->count(instructions) - 1;
    int lastOpInd;
    struct AbstractInstruction* ins;
    for (; index >=0; --index) {
        ins = (struct AbstractInstruction*)
              (instructions->elementAt(instructions, index));
        if (NOP != ins->code) {
            lastOpInd = index;
        }
        ins->index = lastOpInd;
    }

扫描并重定向全部的跳转指令

    struct Iterator* iterator;
    for_each (iterator, instructions) {
        ins = (struct AbstractInstruction*)(iterator->current(iterator));
        if (isJump(ins->code)) {
            struct JumpInstruction* jmp = (struct JumpInstruction*)ins;
            jmp->targetIns = instructions->elementAt(instructions,
                                                     jmp->targetIns->index);
        }
    }

删除 NOP 指令. 任务完成.

    for_each (iterator, instructions) {
        if (NOP == ((struct AbstractInstruction*)
                    (iterator->current(iterator)))->code) {
            revert(iterator->current(iterator));
            iterator->remove(iterator);
        }
    }

 

现在的修改版本中, 输出指令的代码被移入新文件, 减少了 jerry-compiler.c 的混乱程度.

void fakeDefaultAnalyserConsumeNT(void* self, struct AbstractSyntaxNode* node)
{
    if (NULL == node) {
        return;
    }

    node = (struct AbstractSyntaxNode*)newBasicBlockNode(node);
    initStack(&loopStack);
    initialSymTabManager();
    struct List* insList = node->createInstruction(node);
    node->delNode(node);
    finalizeSymTabManager();
    loopStack.finalize(&loopStack);
    if (isFailed()) {
        while (0 != insList->count(insList)) {
            revert(insList->popElementAt(insList, 0));
        }
        cresult = ERROR_IN_SRC;
        return;
    }

    struct NoParamInstruction* endProg = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
    endProg->code = END_PROGRAM;
    insList->addTo(insList, endProg, insList->count(insList));
    /* 原先在这里的代码, 以及 writeInstruction 函数被移入了
       instruction-process.cpp / instruction-process.h */
    ErrMsg err = processIns(insList, treeout);
    if (NULL != err) {
        fputs(err, stderr);
        cresult = IO_ERROR;
    }
    insList->finalize(insList);
}

 

编译器/解释器构造到此结束, 也许我还会改改 Jerry, 不过不保证, 哈~

分享到:
评论
12 楼 lwwin 2010-05-31  
RednaxelaFX 写道
我的见识还是太少了……最近在某在x86上跑的东西的手写汇编里的直线型代码里见到手工插入的nop了……还不是很确定插入nop的原因,但看起来跟跳转指令的对齐没关系。


优化做得事情,总觉得让不熟悉的人们(比如水水=3=)感觉到很诧异,嘿嘿~
BZ好久不来了,偶尔想起来跑来看看~
11 楼 RednaxelaFX 2010-01-02  
我的见识还是太少了……最近在某在x86上跑的东西的手写汇编里的直线型代码里见到手工插入的nop了……还不是很确定插入nop的原因,但看起来跟跳转指令的对齐没关系。
10 楼 NeuronR 2010-01-02  
lwwin 写道
没想到一个NOP把FX也拉来了^^
关于代码……一时间没找到位置- -不知道第二段是在哪个部分来着?

instruction-process.c 71 - 99
9 楼 lwwin 2010-01-02  
没想到一个NOP把FX也拉来了^^

关于代码……一时间没找到位置- -不知道第二段是在哪个部分来着?
8 楼 NeuronR 2009-12-22  
经3楼提醒我也突然意识到了这一点... 流水以及分支失败重入等等那些乱七八糟的事情都是硬件处理的而无需软件去 NOP 干预...
7 楼 RednaxelaFX 2009-12-21  
night_stalker 写道
http://www.powerset.com/explore/semhtml/Hazard_%28computer_architecture%29

唔,fx 的意思是不需要手动插入 nop 是么?

这站不错……收藏了~v~
主要是x86是种保证比较强的体系结构,或者说内存模型的限制比较死,我平时都在看x86上的东西没见过用编译器插nop来保证流水线的正确运行的 =_=||||
6 楼 night_stalker 2009-12-21  
看来是……我意识过剩了……
5 楼 night_stalker 2009-12-21  
http://www.powerset.com/explore/semhtml/Hazard_%28computer_architecture%29

唔,fx 的意思是不需要手动插入 nop 是么?
4 楼 幸存者 2009-12-21  
night_stalker 写道
按照 CPU 流水线的指令结构 …… 有些 NOP 是不能删掉的吧。
有时载入某些资源,会消耗数个指令周期,后面必须用 NOP 去等待载入资源的完成,再对其进行操作,否则就出大问题了 ……

要真是这样,我认为这是CPU设计的一大缺陷。
3 楼 RednaxelaFX 2009-12-21  
night_stalker 写道
按照 CPU 流水线的指令结构 …… 有些 NOP 是不能删掉的吧。
有时载入某些资源,会消耗数个指令周期,后面必须用 NOP 去等待载入资源的完成,再对其进行操作,否则就出大问题了 ……

这个是在哪边能查到参考资料么?我只听说过缓存缺失会自然导致流水线停顿,没听说过流水线停顿是靠nop来实现的……(?)
2 楼 NeuronR 2009-12-21  
night_stalker 写道
按照 CPU 流水线的指令结构 …… 有些 NOP 是不能删掉的吧。
有时载入某些资源,会消耗数个指令周期,后面必须用 NOP 去等待载入资源的完成,再对其进行操作,否则就出大问题了 ……

这个这个, 虚拟机是我自己写的很挫的栈式虚拟机.
1 楼 night_stalker 2009-12-21  
按照 CPU 流水线的指令结构 …… 有些 NOP 是不能删掉的吧。
有时载入某些资源,会消耗数个指令周期,后面必须用 NOP 去等待载入资源的完成,再对其进行操作,否则就出大问题了 ……

相关推荐

    单片机C51中的NOP指令使用经验

    NOP指令为单周期指令,可由晶振频率算出延时时间,对于12M晶振,延时1uS。对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。 方法2: 插入方式: __asm //是两个下划线 { nop; }

    单片机C语言中_nop_函数使用及延时计算

    NOP指令为单周期指令,可由晶振频率算出延时时间,对于12M晶振,延时1uS。 第二种情况是对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。在选择C51中的循环语句时,要注意以下几个问题: 1. 定义的...

    1个NOP延时多上时间,怎么计算?

    而NOP指令本身就是一个指令周期,因此执行一个NOP指令的时间就是1个CPU指令周期的时间。根据前面的分析,这也就是1.67MHz的倒数,即大约0.6微秒(µs)。 因此,在CPU频率为20MHz的情况下,执行一个NOP指令大约需要...

    nop.rar_nop_nop键盘

    标题中的“nop.rar_nop_nop键盘”暗示了我们讨论的主题是关于计算机编程中的NOP指令以及与键盘交互的实现。NOP,全称是"No Operation",在计算机指令集中,它通常表示一个空操作或者无操作指令。NOP指令在程序设计中...

    Ollydbg篇NOP位移法

    4. **插入NOP指令**:在Ollydbg中,选择要替换的跳转指令,右键点击并选择“Patch byte”或“Edit byte”,将跳转指令替换为NOP(`0x90`)。如果需要插入多个NOP,重复此过程。 5. **调整跳转目标**:现在,你需要...

    单片机c语言中nop函数的使用方法和延时计算

    标准的C语言中没有空语句。但在单片机的C语言编程中,经常需要用几个空指令产生短延时的效果。  这在汇编语言中很容易实现,写几个nop就行了。

    单片机C语言中nop函数使用及延时计算.pdf

    NOP指令为单周期指令,可以由晶振频率算出延时时间,对于12M晶振,延时1uS。 在Keil C51中,可以直接调用库函数#include<intrins.h> void _nop_(void);_nop_();来产生一条NOP指令。nop函数的使用可以满足对于延时很...

    Cortex-M0指令集(1)

    处理器使用的是ARMv6-M Thumb指令集,包括大量的32位的使用Thumb-2技术的指令。表7-22列出了Cortex-M0指令和它们的周期数。周期计数以零等待状态的系统为基准

    RISCV常见指令的执行周期表

    - **注释**:nop指令不执行任何操作,主要用于调试目的或作为流水线中的填充指令。 - **用途**:通常用来调整指令执行的时间,例如为了实现特定的同步效果。 2. **addi (Add Immediate)** - **执行周期**:1 - ...

    单片机c语言中nop函数的使用方法和延时计算.pdf

    `,然后使用`_nop_()`函数来产生一条NOP指令,这个函数相当于汇编NOP指令,延时几微秒。 对于延时很短的,要求在us级的,可以采用“_nop_”函数来实现。NOP指令为单周期指令,可以由晶振频率算出延时时间,对于12M...

    nop19中文包nop19中文包nop19中文包nop19中文包

    标题中的“nop19中文包”多次重复,可能是指一个针对NOP19编程语言或框架的中文资源包。NOP19可能是一个特定的版本或者一个专门为学习或开发目的设计的工具。由于信息有限,我们首先假设这是一个编程环境或库的中文...

    南昌大学06年单片机试卷和答案

    2. **延时程序**:给定程序通过MOV、NOP和DJNZ指令实现延时,其中NOP指令用于插入一个机器周期的空闲时间。若要延时50us,需计算R3的初始值以确保执行循环次数正确。 3. **数值相加**:该程序将两个位于内存40H和...

    基于Javascript的Nop平台低代码前端设计源码

    Nop平台低代码前端设计源码:该项目基于Javascript开发,包含1549个文件,主要使用TypeScript、...nop-chaos是Nop平台的前端部分,采用了Vue3.0、ant-design-vue、百度AMIS、logicflow、xspreadsheet等技术实现。

    C语言里如何编写精确的微量延时.doc

    由于C语言的时序性相对较弱,实现精确延时较为困难,通常需要借助汇编语言的空操作(NOP)指令来完成。在AVR C程序中,我们可以通过定义一系列宏来实现这一目标,简化代码书写并确保延时精度。 首先,我们需要为1到...

    1500 SCL 指令用法.pdf

    "1500 SCL 指令用法" 该资源介绍了 Siemens S7-1200 和 S7-1500 PLC 系统中 SCL 指令的使用方法。SCL 指令是一种基于文本的编程语言,用于编写 PLC 程序。 位逻辑运算 SCL 指令提供了多种位逻辑运算,包括与、或、...

    51单片机的常用指令

    NOP指令是一种空操作指令,用于占用 CPU 的时钟周期,但不执行任何操作。 ADD和ADDC指令 ADD和ADDC指令是51单片机中的一种加法指令,用于将两个寄存器或存储器的值相加。ADDC指令用于带 CY 的加法。 SUBB指令 ...

    (完整版)西门子PLC指令教程应用指令.ppt

    空操作指令NOP是程序控制类指令中的一种,用于执行空操作。该指令不影响用户程序的执行,操作数N是标号,是一个0~255的常数。 NOP指令的格式为:NOP N 例如:NOP 30 5.1.2 结束及暂停 结束及暂停指令有两条:END...

    feature-fixups-test.rar_out

    程序员有时会用nop填充代码来替换原有指令,可能是为了调试、优化或者在某些条件下禁用特定代码块。 在"feature-fixups-test.c"这个C语言源文件中,很可能包含了一个或多个函数或代码段,这些代码可能在特定情况下...

    安川机器人指令手册

    NOP功能: NOP指令用于程序开头指令。例如 NOPTIME功能: TIME指令用于暂停时间。例如 T=12.50暂停 12.5 秒后执行下一指令。 CWAIT功能: CWAIT指令用于等待 NWAIT 指令,那一行指令执行完毕才继续执行下一行指令。...

    NPU指令集1

    在FPGA开发中,理解并熟练使用这样的NPU指令集对于优化神经网络的硬件实现至关重要,因为它直接影响到计算速度和功耗效率。通过精心设计指令序列,可以构建出能够高效执行各种神经网络模型的定制化硬件加速器。

Global site tag (gtag.js) - Google Analytics