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

[虚拟机]指令写出

阅读更多

是的, 还有几个节点的指令生成没有测试呢, 包括流控制语句, IO. 不过呢, 流控制是相对比较容易优化的, 进行"生成指令再对比"的测试不太好维护, 而 IO 指令很简单: 所以要不这样, 把指令写出来, 然后开个虚拟机运行, 如果运行没问题就算测试通过了.

语义错误处理

一些常见的语义错误处理在之前的之前的内容中都以注释形式出现, 不过如果留心的话, 上次更新的代码这些错误处理都是完备的, 比如在 jerry-ui.h 和 jerry-ui.c 两个文件中给出了这些错误的 UI, 而 const.h 中更新了很多字符串字面常量宏作为错误信息. 当然你也可以随意修改, 随便它们怎么运作. 这里只是稍带提一下, 以免等会儿突然见到会显得很突兀.

/* 一般语义错误 */
void semanticErr(ErrMsg err, int line)
{
    fprintf(stderr, "Error occurs at line %d:\n"
                    "    %s\n",
                    line, err);
    failed = 1;
}

/* 符号错误* /
void symError(ErrMsg err, struct VariableNode* var)
{
    fprintf(stderr, "Error occurs at variable `%s', line %d:\n"
                    "    %s\n",
                    var->ident, var->line, err);
    failed = 1;
}

参数处理

参数处理目前采用比较笨的方法, 就是逐个参数进行比较. 比如, 现在允许参数有

/* 输出帮助信息 */
#define OPTION_HELP_INFO "--help"
/* 指定目标文件 */
#define OPTION_OUTPUT_SPECIFY "-o"

参数解析循环

 

static int showHelp(int argc, char* argv[]);
static int setOutputFileName(int argc, char* argv[]);
static int setFileToProcess(int argc, char* argv[]);

void handleParam(int argc, char* argv[], FILE** out)
{
    int i = 1;
    if (1 == argc) {
        cmdParamError("Jerry: too few arguments.");
    }
    while (i < argc) {
        if (0 == strcmp(OPTION_HELP_INFO, argv[i])) {
            i += showHelp(argc - i - 1, argv + i + 1);

        } else if (0 == strcmp(OPTION_OUTPUT_SPECIFY, argv[i])) {
            i += setOutputFileName(argc - i - 1, argv + i + 1);

        } else {
            i += setFileToProcess(argc - i, argv + i);
        }
        ++i;
    }

    *out = fopen(outFileName, "wb");
    if (NULL == *out) {
        cmdParamError("output file access error.");
    }
}

每个分支匹配一个参数, 然后传入剩余的参数到具体的处理函数中去. 而每个处理函数的返回值则是它所消耗的参数个数, 以便于循环中调整参数指针.

 

指令写出

其它边边角角的东西不细说了, 现在切换到 jerry-compiler.c 文件中. main 函数中并没有写出的操作, 在调用了 handleParam 之后只是为语法分析进行铺垫工作和资源分配之类的. 嗯, 另有一个地方比 main 函数更适合完成这项工作, 那就是压在语法分析器栈栈底的无名英雄 FakeDefaultAnalyser 的 consumeNonTerminal 函数.

很显然, 当这个函数被调用时, 如果没有任何错误, 那么一定是其它所有分析器都完成任务正常退出了, 因此, 传递给它的语法节点就是表征整个源文件的语法树. 那么只需要将这个节点的指令序列输出就搞定了.

void fakeDefaultAnalyserConsumeNT(void* self, struct AbstractSyntaxNode* node)
{
    if (NULL == node) {
        printf("Nothing returned.\n");
    } else {
        node = (struct AbstractSyntaxNode*)newBasicBlockNode(node);
        initStack(&loopStack);
        initialSymTabManager();
        struct List* insList = node->createInstruction(node);
        finalizeSymTabManager();
        loopStack.finalize(&loopStack);
        if (isFailed()) {
            while (0 != insList->count(insList)) {
                revert(insList->popElementAt(insList, 0));
            }
            fprintf(stderr, "Error in source code.\n");
        } else {
            // 这里写出指令
    }
}
现在要做的是, 首先给每条指令烙上偏移值, 以便于让跳转指令运作 --- 跳转指令都会变为一条整数参数指令.

            InstructionCode code;
            int segOffset = 0;
            struct Iterator* iterator;
            struct NoParamInstruction* endProg = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
            endProg->code = END_PROGRAM;

            // 加入最后一条结束指令
            insList->addTo(insList, endProg, insList->count(insList));
            for_each (iterator, insList) {
                code = ((struct AbstractInstruction*)
                        (iterator->current(iterator)))->code;
                ((struct AbstractInstruction*)
                 (iterator->current(iterator)))->segOffset = segOffset;
                segOffset += sizeof(InstructionCode);

                // 计入参数偏移量
                if (isJump(code) || isIntParamIns(code)) {
                    segOffset += INT_SIZE;
                } else if (isRealParamIns(code)) {
                    segOffset += REAL_SIZE;
                }
            }
然后写出总的指令大小, 或者可以称之为山寨版的代码段总长度

            if (1 != fwrite(&segOffset, sizeof(segOffset), 1, treeout)) {
                fprintf(stderr,
                        "An error occurs while writing instructions to file.\n");
                exit(1);
            }
最后一步, 将每个指令写出

            for_each (iterator, insList) {
                // 实现在下面
                writeInstruction(iterator->current(iterator));
            }
            // 回收指令空间
            while (0 != insList->count(insList)) {
                revert(insList->popElementAt(insList, 0));
            }

/* 逐条指令写出 */
void writeInstruction(void* instruction)
{
    InstructionCode iCode = ((struct AbstractInstruction*)instruction)->code;
    if (1 != fwrite(&iCode, sizeof(InstructionCode), 1, treeout)) {
        fprintf(stderr, "Error occur when flush instructions to file.\n");
        exit(1);
    }
    // 带参数指令写出参数
    if (isIntParamIns(iCode)) {
        if (1 != fwrite(&(((struct IntParamInstruction*)instruction)->param),
                        INT_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    } else if (isRealParamIns(iCode)) {
        if (1 != fwrite(&(((struct RealParamInstruction*)instruction)->param),
                        REAL_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    } else if (isJump(iCode)) {
        // 将指针参数转换为段间偏移量, 写出该偏移量整数
        int offset = ((struct JumpInstruction*)instruction)
                                                ->targetIns->segOffset
                     - ((struct JumpInstruction*)instruction)->segOffset;
        if (1 != fwrite(&offset, INT_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    }
}

请 update Jerry 以获取最新源代码.

分享到:
评论
2 楼 NeuronR 2009-11-15  
啊, 是的...
1 楼 lwwin 2009-11-15  
最后的这个 fakeDefaultAnalyserConsumeNT
是不是就是以前测试程序中 newFakeDefaultAnalyser 这个实例的 consumeNonTerminal 接口实现? 其他各个部分不变就可以了么^^?

相关推荐

    《自己动手实现Lua:虚拟机、编译器和标准库》_张秀宏_2018-9-27.rar

    *部分主要讨论LuaAPI和虚拟机实现,包括二进制chunk格式、Lua虚拟机指令集、元编程、错误处理等。第二部分主要讨论Lua语法和编译器实现,包括词法分析、语法分析、代码优化、代码生成等。第三部分主要讨论Lua辅助API...

    D Parser 之前:写一个简单的虚拟机

    标题中的“D Parser 之前:写一个简单的虚拟机”指的是在构建D语言解析器之前,先设计和实现一个简单的虚拟机。虚拟机是许多编程语言解释执行的基础,它模拟了真实的计算机硬件,允许编译后的代码在不同平台上运行。...

    Java虚拟机规范中文版

    了解和掌握Java虚拟机规范对于Java开发者来说是非常有必要的,这不仅可以帮助开发者写出更加规范的代码,还能让他们更好地理解和优化Java程序的性能,以及使用JVM提供的高级特性。对于实现Java虚拟机的技术人员来说...

    C++虚拟机乘法

    题目中的两个乘法算法示例,分别展示了如何在虚拟机指令集的限制下,通过循环和累加实现乘法运算。下面,我们将对这两个方法进行详细分析。 ##### 方法1详解: 1. **初始化**:设置寄存器C和D为0,准备用于累加...

    My-C编译器&虚拟机

    器,它的目标平台是一款馆主编写的虚拟机;而目标代码也是经过 馆主加强的一套工业汇编指令。但这不能妨碍My-C成为一款非常好 玩的C语言编译器。 它实现了95%以上的C89语法,并添加改造了有趣的内嵌汇编语言;它...

    控制qemu虚拟机.zip易语言项目例子源码下载

    QEMU支持动态翻译,通过将目标代码转换为宿主机上的机器指令来实现快速执行。此外,QEMU还可以配合KVM(Kernel-based Virtual Machine)模块,实现半虚拟化,以提高性能。 二、易语言控制QEMU虚拟机 易语言以其直观...

    深入java虚拟机 高清pdf 高清高清高清

    通过学习,开发者能够避免常见的性能瓶颈,编写出更高效的代码。 7. **JVM调优工具**:介绍了如JConsole、VisualVM、JProfiler等用于监控和诊断JVM状态的工具,以及如何使用这些工具进行问题定位和性能分析。 8. *...

    sun JAVA虚拟机

    Sun JAVA虚拟机,全称Java Virtual Machine,是Java平台的核心组成部分,它负责解析并执行Java程序的字节码,使得Java代码能够...对于Java程序员来说,深入理解和掌握JVM的工作原理,有助于写出更高效、更稳定的代码。

    dongfeng.rar_C 虚拟机_MIPS

    虚拟机是一种软件模拟的计算机,它可以运行在真实的硬件上,模拟出另一种计算机环境,使得特定的软件可以在不同的操作系统或硬件平台上运行。C语言实现的MIPS虚拟机,就是通过C语言代码来模拟MIPS处理器的指令集和...

    学习java的第一步,就是了解Java虚拟机

    这可以帮助开发者写出更高效的代码,并确保应用程序能够在各种环境下稳定运行。 #### Java虚拟机的基础知识 - **数据类型支持**:JVM支持多种基本数据类型,包括但不限于: - `byte`:占用1字节 - `short`:占用...

    java虚拟机深入了解

    Java虚拟机(JVM)是Java编程语言的核心组成...深入理解JVM不仅有助于写出高效稳定的Java程序,还能帮助开发者解决复杂的运行时问题。通过持续学习和实践,开发者能够更好地驾驭Java虚拟机,提高软件系统的整体性能。

    PhoenixII手机脚本系统(附带虚拟机源码, 使用教程和脚本完整例子)

    本脚本系统主要面向内存和... 编译效率非常高(编译出的指令集接近手写!), 同时提供虚拟机源码, 可以随时按照自己的需求改进虚拟机, 附送29页完整教程和一个现成的J2ME演示包. 看完后能使你的手机项目架构上多一种选择.

    使用Java写的GVmaker虚拟机(开源)

    【标题】"使用Java写的GVmaker虚拟机(开源)"是一个关于使用Java编程语言开发的GVmaker虚拟机项目,该项目是开源的,意味着它的源代码可供公众查看、学习和改进。这个虚拟机可能是为了理解虚拟机的工作原理,或者作为...

    java虚拟机

    高效实现:探索 Java 编译器,以及内嵌于 Java 虚拟机中的即时编译器,帮助你更好地理解 Java 语言特性,继而写出简洁高效的代码; 代码优化:介绍如何利用工具定位并解决代码中的问题,以及在已有工具不适用的情况...

    易语言检测程序是不是在虚拟机中运行的代码

    在 `_检测按钮_被单击` 子程序中,如果 `检测虚拟机` 返回 `真`,程序会弹出一个对话框提示用户程序不能在虚拟机中运行;如果返回 `假`,则提示程序正在物理机上运行。 总的来说,这段代码提供了在易语言环境中检测...

    JVM虚拟机,经典java虚拟机

    这些知识能够帮助开发者写出更高效、更稳定的Java应用。 另外,JVM的规范和实现经历了多个版本的发展,例如JDK 8和JDK 11中有重大更新。每个版本都带来了新的特性,比如JDK 8中的Lambda表达式、新的日期时间API,...

    虚拟机VMWare Tool的安装和共享文件设置

    可以看到 fc4_iso 的目录,并且可以用 cp 等指令实现 Windows 到 Linux,Linux 到 Windows 的读写操作了。 VMWare Tool 的安装和共享文件设置可以增强虚拟机的性能和功能,方便虚拟机和主机之间的文件共享。

Global site tag (gtag.js) - Google Analytics