精华内容
下载资源
问答
  • OpenCL程序是由一组内核组成,而这些内核就是程序源码中以限定符__kernel声明的函数。__kernel函数可能会用到一些辅助函数以及常量数据,他们也是程序的一部分。 1、创建程序对象 cl_program ...
    OpenCL程序是由一组内核组成,而这些内核就是程序源码中以限定符__kernel声明的函数。__kernel函数可能会用到一些辅助函数以及常量数据,他们也是程序的一部分。
    

    1、创建程序对象
    cl_program clCreateProgramWithSource(cl_context context,
    cl_uint count,
    const char **strings,
    const size_t *lengths,
    cl_int *errcode_ret)
    这个函数可以创建一个程序对象,并将strings中的源码装入到这个对象中。此程序对象关联的设备就是context所关联的设备。strings中的源码是OpenCL C程序源码、文档,或者是自定义的代码。
        context 是一个OpenCL上下文。
        strings 是一个字符串序列,有count个元素,字符串可选的以null结束。这些字符串就构成了源码。
        lengths 也是一个序列,其中的元素代表每个字符串中的字符个数(字符串的长度)。如果某个元素是零,则相应的字符串以null终止。如果lengths是NULL,则strings中的所有字符串都已null终止。所有大于零的值都不包括null终止符。
        errcode_ret会返回相应的错误码。如果为NULL,不返回错误码。

    如果clCreateProgramWithSource成功创建了程序对象,则返回该对象,并将errcode_ret置为CL_SUCCESS。否则返回NULL,并将errcode_ret置为下面的错误码之一:
    • CL_INVALID_CONTEXT,context无效
    • CL_INVALID_VALUE,count是零,或者strings中任一项是NULL
    • CL_DEVICE_TYPE_DEFAULT:系统中默认的OpenCL设备。不能是CL_DEVICE_TYPE_CUSTOM类型的设备。
    • CL_DEVICE_TYPE_ALL:系统中所有的OpenCL设备。不包括CL_DEVICE_TYPE_CUSTOM类型的设备。
    2、编译程序对象
    cl_int clBuildProgram(
    cl_program program,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *options,
    void (CL_CALLBACK *pfn_notify)(cl_program program,
    void *user_data),
    void *user_data)
    • program  是程序对象。
    • device_list指向program所关联的设备。如果device_list是NULL,则会为program所关良的所有设备创建程序对象的执行体。否则,仅为此清单中的设备构建程序执行体。
    • num_devices即device_list中的设备数目。
    • options指向一个null终止的字符串,用来描述构建选项。
    • pfn_notify是应用所注册的回调函数。在构建完程序执行体后会被调用(不论成功还是失败)。如果pfn_notify不是NULL,一旦开始构建,clBuildProgram会立刻返回,而不必等待构建完成。如果上下文、所要编译连接的函数、设备清单以及构建选项都是有效的,以及实施构建所需的主机和设备资源都可用,那么就可以开始构建了。如果pfn_notify是NULL,直到构建完毕,clBuildProgram才会返回。对此函数的调用可能是异步的。应用需要保证此函数是线程安全的。
    • user_data在调用pfn_notify时作为参数传入,可以是NULL。
    如果执行成功,则clBuildProgram会返回CL_SUCCESS。否则返回下列错误码之一:
    • CL_INVALID_PROGRAM,无效的program。
    • CL_INVALID_VALUE,device_list是NULL而num_devices大于零,或者device_list不是NULL而num_devices等于零。
    • CL_INVALID_VALUE,pfn_notity是NULL,而user_data不是NULL。
    • CL_INVALID_DEVICE,device_list中的OpenCL设备不是program所关联的设备
    • CL_INVALID_BINARY,program是用clCreateProgramWithBinary所创建的,但是没有为device_list中的设备装载相应的程序二进制妈。
    • CL_INVALID_BUILD_OPTIONS,options所制定的构建选项无效。
    • CL_INVALID_OPERATION,对于device_list中任一设备而言,之前调用clBuildProgram为program构建所对应的程序执行体的动作还未完成。
    • CL_COMPILER_AVAILABALE,如果是用clCreateProgramWithSource创建的program,但是没有可用的编译器,即CL_DEVICE_COMPILER_AVAILABLE是CL_FALSE
    • CL_BUILD_PROGRAM_FAILURE,构建程序对象失败。如果clBuildProgram没有将此错误返回,则在构建完成时会将其返回。
    • CL_INVALID_OPERATION,有附着到program上的内核对象。
    • CL_INVALID_OPERATION,program不是由clCreateProgramWithSource|Binary创建的。
    • CL_OUT_OF_RESOURCES,为设备上的OpenCL操作分配内存失败。
    • CL_OUT_OF_HOST_MEMORY,为主机上的OpenCL操作分配内存失败。

    3、查询程序对象信息
    cl_int clGetProgramInfo(cl_program program,
    cl_program_info param_name,
    size_t param_value_size,
    void *param_value,
    size_t *param_value_size_ret)
    • program,所要查看的程序对象。
    • param_name,指定所要查看的信息。
    • param_value,指向用来存放查询结果的内存。如果为NULL,则忽略。
    • param_value_size,param_value所指内存的大小。其值必须大于param_name所指定的返回类型。
    • param_value_size_ret,返回查询结果的实际大小。如果为NULL,则忽略。
    clGetProgramInfo所支持的param_name
    cl_program_info 返回类型
    CL_PROGRAM_REFERENCE_COUNT cl_int
    返回program的引用计数
    CL_PROGRAM_CONTEXT cl_context
    返回创建程序对象时所指的上下文。
    CL_PROGRAM_NUM_DEVICES cl_uint
    返回program所关联的设备数幕
    CL_PROGRAM_DEVICES cl_device_id[]
    返回program所关联的设备。可以是创建程序对象时所用上下文中的设备,也可以使clCreateProgramWithBinary所指定的设备中的一个子集
    CL_PROGRAM_SOURCE char[]
    返回传给clCreateProgramWithSource的程序源码。返回的字符串中将所有的源码串在一起,并以null终止。
    如果program是用clCreateProgramWithBinary或者clCreateProgramWithBuiltinkernels创建的,可能返回空字符串。或者对应的程序源码,这取决于二进制码中是否有程序源码
    param_value_size_ret中会返回源码中字符的实际数目,包含null终止字符。
    CL_PROGRAM_BINARY_SIZES size_t[]
    返回一个数组,内含program所关联的所有设备的程序二进制码的大小。数组的大小等于program所关联的设备的个数。如果任一设备没有对应的二进制码,则返回零。
    如果program是用clCreateProgramWithBuiltinKernels创建的,可能数组中的所有元素都是零。
    CL_PROGRAM_BINARIES unsigned char *[]
    返回一个数组,内涵program所关联的所有设备的二进制码。
    param_value指向一个包含n个指针的数组,所有指针都有调用者分配,其中n是program所关联的设备的数目。对于每个指针分配多少内存,可以通过CL_PROGRAM_BINARY_SIZE来查询。
    CL_PROGARM_NUM_KERNELS size_t
    返回program中声明的内核总数。对于program所关联的设备来说,至少要为其中之一成功构建了可执行程序对象,然后才能使用此资讯。
    CL_PROGRAM_KERNEL_NAMES char []
    返回program中内核的名字,以分号间隔。对于progarm所关联的设备来说, 至少要为其中之一成功构建了可执行程序对象,然后才能使用此资讯。   

    如果执行成功,clGetProgramInfo会返回CL_SUCCESS,否则返回下列错误码之一:
    • CL_INVALID_VALUE,parma_name无效,或者param_value_size小于param_name的返回类型的大小,且param_value不是NULL。
    • CL_INVALID_PROGRAM,program无效。
    • CL_INVALID_PROGRAM_EXECUTABLE,param_name是CL_PROGRAM_NUM_KERNELS 或者CL_PROGRAM_KERNEL_NAMES,并且在program所关联的设备中至少有一个设备所对应的程序执行体没有成功构建。
    • CL_OUT_OF_RESOURCES,为设备上的OpenCL操作分配内存失败。
    • CL_OUT_OF_HOST_MEMORY,为主机上的OpenCL操作分配内存失败。
    4、查询编译程序信息
    cl_int clGetProgramBuildInfo (cl_program program,
    cl_device_id device,
    cl_program_build_info param_name,
    size_t param_value_szie,
    void *param_value,
    size_t *param_value_size_ret)
    • program,所要查询的程序对象。
    • device,指定要查询那个设备的构建信息。device必须是program所关联的设备。
    • param_name,指定要查询什么信息。
    • param_value,指向的内存用来存放查询结果。如果为NULL,可以忽略。
    • param_value_size,param_size所指向的内存的大小。其值必须大于对应的param_name所返回的类型的大小。
    • param_value_size_ret,返回查询结果的实际大小。如果为NULL,则忽略。
    如果执行成功,clGetProgramBuildInfo会返回CL_SUCCESS。否则,返回下列错误吗之一:
    • CL_INVALID_DEVICE,device不是与program相关联的设备。
    • CL_INVALID_VALUE,param_name无效,或者param_value_size小于相应的param_name返回类型的大小,且param_value不是NULL。
    • CL_INVALID_PROGRAM,program无效。
    • CL_OUT_OF_RESOURCES,为设备上的OpenCL操作分配内存失败。
    • CL_OUT_OF_HOST_MEMORY,为主机上的OpenCL操作分配内存失败。

    clGetProgramBuildInfo所支持的param_name
    cl_program_build_info 返回类型
    CL_PROGRAM_BUILD_STATUS cl_build_status
    返回构建、编译或链接的状态,在program上为device最后事实的那个(clBuildProgram、clCompileProgram或clLinkProgram)。
    可以是下列之一:
    • CL_BUILD_NONE,三个步骤都没有实施。
    • CL_BUILD_ERROR,最后实施的那个出错。
    • CL_BUILD_SUCCESS,最后实施的那个成功。
    • CL_BUILD_IN_PROGRESS,最后实施的那个还未完成。
    CL_PROGRAM_BUILD_OPTIONS char[]
    返回构建、编译或链接的选项,即最后实施的那个的参数options。如果状态是CL_BUILD_NONE,则返回空字串。
    CL_PROGRAM_BUILD_LOG char []
    返回最后实施的那个的日志。如果状态是CL_BUILD_NONE,则返回空字符串。
    CL_PROGRAM_BINARY_TYPE cl_program_binary_type
    返回device所对应的二进制码。可以是下列之一:
    • CL_PROGRAM_BINARY_TYPE_NONE,没有对应的二进制码。
    • CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT,有编译过的二进制码。如果program是使用clCreateProgramWithSource创建并使用clComplieProgram编译的,或者是用clCreateProgramWithBinary装载的编译过的二进制码,都是这种情况。
    • CL_PROGRAM_BINARY_TYPE_LIBRARY,有庫的二元碼。如果用 clLinkProgram 創建 program 時指定了鏈接選項 -create-library,或者是用 clCreateProgramWithBinary 裝載的庫的二元碼,都屬於這種情況。
    • CL_PROGRAM_BINARY_TYPE_EXECUTABLE,有可執行的二元碼。如果用 clLinkProgram 創建 program 時沒有指定鏈接選項 -create-library,或者是用 clCreateProgramWithBinary 裝載的可執行的二元碼,都屬於這種情況。

    5、创建程序的另一种方法
    cl_program clCreateProgramWithBinary (cl_context context,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const unsigned char **binaries,
    cl_int *binary_status,
    cl_int *errcode_ret)
    clCreateProgramWithBinary 会创建一个程序对象,并将binary中的内容装载到此对象中。
    cl_program clCreateProgramWithBuiltInKernels(
    cl_context context,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *kernel_names,
    cl_int *errcode_ret)
    clCreateProgramWithBuiltInKernels 会创建一个程序对象,并将内建内核的相关资讯装载到此对象中。  
      
    6、编译和链接的分离
    • 编译阶段、链接阶段的分离。编译阶段可以将程序源码编译成二进制目标码,而链接阶段可以将其与其他目标码链接成程序执行体。
    • 潜入头文件。。对于程序源码所包含的头文件来说,构建选项 -I可以用来指定搜索路径。 也可以在目标码中内嵌头文件的源码。
    • 库。可以使用连接器将目标码和库连接成程序执行体,或者创建一个新的库。
    cl_int clCompileProgram(
    cl_program program,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *options,
    cl_uint num_input_headers,
    const cl_program *input_headers,
    const char **header_include_names,
    void (CL_CALLBACK *pfn_notify)(
    cl_program program,
    void *user_data),
    void *user_data)
    这个函数是用来编译程序源码的。便以前会记性预处理。编译工作的目标设备是program所关联的所有设备或那些指定的设备。

    cl_program clLinkProgram (cl_context context,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *options,
    cl_uint num_input_programs,
    const cl_program *input_programs,
    void (CL_CALLBACK *pfn_notify)(
    cl_program program,
    void *user_data),
    void *user_data,
    cl_int *errcode_ret)
    这个函数可以把一组目标码和库链接成执行体,并创建一个包含这个执行体的程序对象。

    7、编译选项

    预处理选项:
    • -D name
      • 预定义宏name,其值为1。
    • -D name = definition
      • 预定义宏name为definition。
    • -I dir
      • 在头文件搜索路径中包含目录dir
    浮点数选项:
    • -cl-single-precision-constant
      • 将双精度浮点常数视为单精度浮点常数。
    • -cl-demorms-are-zero
      • 对于双精度和单精度数,这个选项指定非规格化数可以刷新为零
    • -cl-fp32-correctly-rounded-divide-sqrt
      • 对于程序源码中的单精度浮点除法和sqrt而言,应用可以使用此选项来保证对它们的正确舍入。
    优化选项:
    • -cl-opt-disable
      • 禁用所有优化。缺省情况下优化是打开的。
    • -cl-mad-enable
      • 允许mad代替a*b+c。mad会用较低的精确度来计算a*b+c。
    • -cl-no-signed-zeros
      • 允许在进行浮点运算时忽略零的正负。
    • -cl-unsafe-math-optimizations
      • 允许对浮点算术的优化:
        • 假定参数和结果都是有效的;
        • 可能违反IEEE754的标准
        • 可能违反对梵净度浮点数的数值符合性的要求
      • 此选项包含了-cl-no-signed-zeros和-cl-mad-enable。
    • -cl-finite-math-only
      • 在执行浮点算术运算时,可以假定参数和结果不是NAN或这无穷大无穷小。
    • -cl-fast-relaxed-math
      • 相当于-cl-finite-math-only和-cl-unsafe-math-optimizations的组合。
    请求或抑制警告的选项
    • -w
      • 禁止所有的警告信息。
    • -Werror
      • 把所有警告都当成错误。
    控制OpenCL C版本的选项
    • -cl-std = version
      • 确定所使用的OpenCL C语言的版本。
    展开全文
  • iOS 进阶:【什么是LLVM?】LLVM架构的主要组成部分、llvm 编译的整个流程介绍

    前言

    llvm 是一系列 分模块和可重用的编译工具链,他提供了一种代码编写良好的中间表示(IR),可以作为多种语言的后端,还可以提供与编程无关的优化和针对多种CPU的代码生成功能。

    • LLVM架构的主要组成部分:

    1)前端:前端用来获取源代码然后将它转变为某种中间表示,我们可以选择不同的编译器来作为LLVM的前端,如gcc,clang。

    2)Pass(通常翻译为“流程”):Pass用来将程序的中间表示之间相互变换。一般情况下,Pass可以用来优化代码,这部分通常是我们关注的部分。

    3)后端:后端用来生成实际的机器码

    The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.

    I 、LLVM的架构

    1.1 传统的编译器的架构如下

    在这里插入图片描述

    1.2 LLVM的架构

    • LLVM的架构

    当编译器需要支持多种源代码和目标架构时,基于LLVM的架构,设计一门新的语言只需要去实现一个新的前端就行了,支持新的后端架构也只需要实现一个新的后端就行了。其它部分完成可以复用,就不用再重新设计一次了。

    在这里插入图片描述

    1.3基于LLVM,我们可以做什么?

    • 做语法树分析,实现语言转换OC转Swift、JS or 其它语言,字符串加密
    • 编写ClangPlugin,命名规范,代码规范,扩展功能。
    • 编写Pass,代码混淆优化。

    基于LLVM进行代码混淆时,只需关注中间层表示(IR)

    II、安装编译LLVM

    2.1 下载

    1.直接从官网下载:http://releases.llvm.org/download.html
    2.svn获取

    svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
    
    cd llvm/tools
    svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
    cd ../projects
    svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
    cd ../tools/clang/tools
    svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
    
    

    3.git获取

    git clone http://llvm.org/git/llvm.git
    cd llvm/tools
    git clone http://llvm.org/git/clang.git
    cd ../projects
    git clone http://llvm.org/git/compiler-rt.git
    cd ../tools/clang/tools
    git clone http://llvm.org/git/clang-tools-extra.git
      
    
    

    2.2 编译

    最新的LLVM只支持cmake来编译了,首先安装cmake。

    brew install cmake
    
    

    编译:

    mkdir build
    cmake /path/to/llvm/source
    cmake --build .
    
    

    编译生成Xcode项目的命令

    mkdir build
    cd build
    cmake -G xcode CMAKE_BUILD_TYPE="Debug" ../llvm
    open LLVM.xcodeproj
    
    

    打开Xcode的时候,选择munually manage sechme,选择需要编译的Target(clang),接下来执行command+R

    编译完成后,就能在build/bin/目录下面找到生成的工具了。

    clang 的版本和Xcode的版本不一样,苹果在开源的LLVM中增加了自己的修改

    III、介绍如何编写、编译、加载、运行一个pass

    pass 处理编译过程中代码的额转换以及优化工作。所有的pass都是pass的子类,不同的passs实现不同的作用,可以分别继承不同的pass类实现对应的功能

    在这里插入图片描述

    3.1 配置编译环境

    在lib/Transforms/新建一个文件夹 Hello

    配置编译脚本去编译源代码CMakeLists.txt

    add_llvm_loadable_module( LLVMHello
      Hello.cpp
        
      DEPENDS
      intrinsics_gen
      PLUGIN_TOOL
      opt
      )
    
    

    修改…/Transforms/CMakeLists.txt, 加上刚刚写的模块hello

    add_subdirectory(hello)
    
    

    编译脚本指定使用编译源文件 Hello.cpp 生成 $(LEVEL)/lib/LLVMHello.dylib 动态库,该文件可以被opt 通过-load 参数动态加载。

    在这里插入图片描述

    open Xcode 重新build,在项目里面添加LLVMHello 的target为secheme;在过程中找到 loadable modules 下面LLVMHello 里面的hello.cpp ,开始编写代码。

    3.2 编写代码

    • 编写一个pass操作function,输出一些相关信息,需要使用如下头文件
    #include "llvm/IR/Function.h"
    
    #include "llvm/Pass.h"
    
    #include "llvm/Support/raw_ostream.h"
    
    
    
    
    • 这些头文件里面的方法属于LLVM命名空间,需要声明使用LLVM命名空间
    using namespace llvm;
    
    

    在这里插入图片描述

    3.2.1 编写一个匿名空间

    C++的匿名空间和c的static 关键词一样,定义在匿名空间中的变量仅对当前文件可见。

    namespace {
      // Hello - The first implementation, without getAnalysisUsage.
      struct Hello : public FunctionPass {// 定义hello类继承自FunctionPass;功能不同的pass 会继承对应的父类
        static char ID; // Pass identification, replacement for typeid 定义可供LLVM 标示的passID
        Hello() : FunctionPass(ID) {}
        
        bool runOnFunction(Function &F) override {//定义一个runOnFunction 重载继承父类的抽象虚函数,这个函数里面可以针对函数进行特殊处理(打印每个方法的名字)。
          ++HelloCounter;
          errs() << "Hello: ";
          errs().write_escaped(F.getName()) << '\n';
          return false;
        }
      };
    }
    
    

    3.2.2 初始化passID

    因为LLVM将使用ID的地址去识别一个pass,因此初始化的value不重要

    char Hello::ID = 0;
    
    

    3.2.3 注册pass hello

    指定命令行参数为“hello”,名字说明为“hello world pass”

    static RegisterPass<Hello> X("hello", "Hello World Pass");
    
    

    3.2.4 生成 .dylib

    选中target LLVMhello 执行command+B,生成build/debug/lib/LLVMHello.dylib

    3.2.5 例子

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
    #include "llvm/Analysis/LoopInfo.h"
    
    using namespace llvm;
    
    namespace{
        struct Hello : public FunctionPass{
            static char ID;
            
            Hello() : FunctionPass(ID){}
            
            virtual void getAnalysisUsage(AnalysisUsage &AU) const override{
                AU.addRequired<LoopInfoWrapperPass>();
                AU.setPreservesAll();
            }
            
            bool runOnFunction(Function &F) override{
                errs() << "Hello: ";
                errs().write_escaped(F.getName()) << "\n";
                
                LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
                int loopCounter = 0;
                for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i) {
                    loopCounter++;
                }
                
                errs() << "loop num:" << loopCounter << "\n";
                
                return false;
            }
        };
    }
    
    char Hello::ID = 0;
    
    static RegisterPass<Hello> X("hello", "hello world pass", false, false);
    
    • Hello2 World Pass (with getAnalysisUsage implemented)
    namespace {
      // Hello2 - The second implementation with getAnalysisUsage implemented.
      struct Hello2 : public FunctionPass {
        static char ID; // Pass identification, replacement for typeid
        Hello2() : FunctionPass(ID) {}
        
        bool runOnFunction(Function &F) override {
          ++HelloCounter;
          errs() << "Hello: ";
          errs().write_escaped(F.getName()) << '\n';
          return false;
        }
        
        // We don't modify the program, so we preserve all analyses.
        void getAnalysisUsage(AnalysisUsage &AU) const override {
          AU.setPreservesAll();
        }
      };
    }
        
    char Hello2::ID = 0;
    static RegisterPass<Hello2>
    Y("hello2", "Hello World Pass (with getAnalysisUsage implemented)");
    
    

    3.3 使用opt 加载动态库,并指定参数

    之前在代码中通过RegisterPass 注册了pass,现在可以准备一个测试用的源文件,通过opt -load 命令去加载动态库并指定参数hello

    #include <stdio.h>
    int add(int x, int y) {
        return x + y;
    }
    int main(){
        printf("%d",add(3,4));
        return 0;
    }
    
    
    • 编译源文件 生成bitcode: 因为Pass是作用于中间代码,所以我们首先要生成一份中间代码
    path/to/build/deBug/bin/clang -emit-llvm -c test.mm -o test.bc
    
    
    • 然后加载Pass

    在Xcode中将opt的target添加到scheme中。编辑scheme的启动参数,按command+R执行。

    ../build/bin/opt -load ../build/lib/LLVMHello.dylib -simplepass < test.bc > after_test.bc
    
    
    • 通过-help 参数查看注册的pass参数
    ./opt -load ../lib/LLVMHello.dylib -help
    
    
    • passManager 提供了-time-passes参数用于输出pass的时间占比
    ./opt -load ../lib/LLVMHello.dylib -hello -time-passes -disable-output ~/LLVM/test.bc
    
    
    • 编辑/llvm-3.9.0.src/tools/opt/CMakeLists.txt 将LLVMHello模块添加到opt 的依赖,这样在执行opt的时候能自动检测LLVMHello模块有没有被修改,如果被修改了。需要重新编译LLVMHello模块
    add_llvm_tool(opt
      AnalysisWrappers.cpp
      BreakpointPrinter.cpp
      GraphPrinters.cpp
      NewPMDriver.cpp
      PassPrinters.cpp
      PrintSCC.cpp
      opt.cpp
      
      
    DEPENDS
    LLVMHello
      )
    
    

    see also

    //=- PassPrinters.h - Utilities to print analysis info for passes -*- C++ -*-=//
    //
    //                     The LLVM Compiler Infrastructure
    //
    // This file is distributed under the University of Illinois Open Source
    // License. See LICENSE.TXT for details.
    //
    //===----------------------------------------------------------------------===//
    ///
    /// \file
    /// \brief Utilities to print analysis info for various kinds of passes.
    ///
    //===----------------------------------------------------------------------===//
    
    #ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H
    #define LLVM_TOOLS_OPT_PASSPRINTERS_H
    
    #include "llvm/IR/PassManager.h"
    
    namespace llvm {
    
    class BasicBlockPass;
    class CallGraphSCCPass;
    class FunctionPass;
    class ModulePass;
    class LoopPass;
    class PassInfo;
    class raw_ostream;
    class RegionPass;
    class Module;
    
    FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out,
                                            bool Quiet);
    
    CallGraphSCCPass *createCallGraphPassPrinter(const PassInfo *PI,
                                                 raw_ostream &out, bool Quiet);
    
    ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out,
                                        bool Quiet);
    
    LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out,
                                    bool Quiet);
    
    RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out,
                                        bool Quiet);
    
    BasicBlockPass *createBasicBlockPassPrinter(const PassInfo *PI,
                                                raw_ostream &out, bool Quiet);
    
    } // end namespace llvm
    
    llvm::ModulePass *createDebugifyPass();
    
    struct NewPMDebugifyPass : public llvm::PassInfoMixin<NewPMDebugifyPass> {
      llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
    };
    
    llvm::ModulePass *createCheckDebugifyPass();
    
    struct NewPMCheckDebugifyPass
        : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> {
      llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
    };
    
    #endif // LLVM_TOOLS_OPT_PASSPRINTERS_H
    
    展开全文
  • 编译概述与引论

    千次阅读 2016-07-20 20:02:08
    本博文中,介绍编译程序的基本概念,概述编译过程和编译程序结构,编译程序和程序设计环境以及编译程序的生成过程和构造工具知识。什么叫编译程序通常,我们所说的翻译程序是指这样的一个程序,它能够把某一种语言...

    本博文中,介绍编译程序的基本概念,概述编译过程和编译程序结构,编译程序和程序设计环境以及编译程序的生成过程和构造工具知识。

    什么叫编译程序

    通常,我们所说的翻译程序是指这样的一个程序,它能够把某一种语言程序(称为源语言程序)转换成另一种语言程序(称为目标语言程序),而后者与前者在逻辑上是等价的。如果源语言是诸如FORTRAN、Pascal、C、Ada、Smalltalk或Java这样的“高级语言”,而目标语言是诸如汇编语言或机器语言之类的“低级语言”,这样的一个翻译程序就称为编译程序

    高级语言程序除了像上面所说的先编译后执行外,有时也可“解释”执行。一个源语言的解释程序是这样的程序,它以该语言写的源程序作为输入,但不产生目标程序,而是边解释边执行源程序本身。实际上,许多编译程序的构造与实现技术同样适用于解释程序。

    根据不同的用途和侧重,编译程序还可进一步分类。专门用于帮助程序开发和调试的编译程序称为诊断编译程序(Diagnostic Compiler),着重于提高目标代码效率的编译程序叫优化编译程序(Optimizing Compiler)。现在很多编译程序同时提供了调试、优化等多种功能,用户可以通过“开关”进行选择。运行编译程序的计算机称宿主机,运行编译程序所产生目标代码的计算机称目标机。如果一个编译程序产生不同于其宿主机的机器代码,则称它为交叉编译程序(Cross Compiler)。如果不需重写编译程序中与机器无关的部分就能改变目标机,则称该编译程序为可变目标编译程序(Retargetable Compiler)

    世界上第一个编译程序—FORTRAN编译程序是20世纪50年代中期研制成功的。当时,人们普遍认为设计和实现编译程序是一件十分困难、令人生畏的事情。经过40年的努力,编译理论与技术得到迅速发展,到目前为止,现在已形成了一套比较成熟的、系统化的理论与方法,并且开发出了一些好的编译程序的实现语言、环境与工具。在此基础上设计并实现一个编译程序不再是高不可攀的事情。

    编译过程概述

    编译程序的工作,从输入源程序开始到输出目标程序为止的整个过程,是非常复杂的。但就其过程而言,它与人们进行自然语言之间的翻译有许多相近之处。当我们把一种文字翻译为另一种文字,例如把一段英文翻译为中文时,通常需经下列步骤:

    1. 识别出句子中的一个个单词;
    2. 分析句子的语法结构;
    3. 根据句子的含义进行初步翻译;
    4. 对译文进行修饰;
    5. 写出最后的译文。

    类似地,编译程序的工作过程一般也可以划分为五个阶段:词法分析、语法分析、语义分析及中间代码生成、优化、目标代码生成

    第一阶段,词法分析。 词法分析的任务是:输入源程序,对构成源程序的字符串进行扫描和分解,识别出一个个的单词(亦称单词符号或简称符号),如基本字(begin、end、if、for、while等),标识符、常数、算符和界符(标点符号、左右括号等等)。单词符号是语言的基本组成成分,是人们理解和编写程序的基本要素。识别和理解这些要素无疑也是翻译的基础。如同将英文翻译成中文的情形一样,如果你对英语单词不理解,那就谈不上进行正确的翻译。在词法分析阶段的工作中所依循的是语言的词法规则(或称构词规则)。描述词法规则的有效工具是正规式和有限自动机。

    第二阶段,语法分析。 语法分析的任务是:在词法分析的基础上,根据语言的语法规则,把单词符号串分解成各类语法单位(语法范畴),如“短语”、“子句”、“句子”(“语句”)、“程序段”和“程序”等。通过语法分析,确定整个输入串是否构成语法上正确的“程序”。语法分析所依循的是语言的语法规则。语法规则通常用上下文无关文法描述。词法分析是一种线性分析,而语法分析是一种层次结构分析。

    第三阶段,语义分析与中间代码产生。 这一阶段的任务是:对语法分析所识别出的各类语法范畴,分析其含义,并进行初步翻译(产生中间代码)。这一阶段通常包括两个方面的工作。首先,对每种语法范畴进行静态语义检查,例如,变量是否定义、类型是否正确等等。如果语义正确,则进行另一方面工作,即进行中间代码的翻译。这一阶段所依循的是语言的语义规则。通常使用属性文法描述语义规则。

    “翻译”仅仅在这里才开始涉及到。所谓“中间代码”是一种含义明确、便于处理的记号系统,它通常独立于具体的硬件。这种记号系统或者与现代计算机的指令形式有某种程度的接近,或者能够比较容易地把它变换成现代计算机的机器指令。一般而言,中间代码是一种独立于具体硬件的记号系统。常用的中间代码,除了四元式之外,还有三元式、间接三元式、逆波兰记号和树形表示等等。

    第四阶段,优化。 优化的任务在于对前段产生的中间代码进行加工变换,以期在最后阶段能产生出更为高效(省时间和空间)的目标代码。优化的主要方面有:公共子表达式的提取、循环优化、删除无用代码等等。有时,为了便于“并行运算”,还可以对代码进行并行化处理。优化所依循的原则是程序的等价变换规则。

    第五阶段,目标代码生成。这一阶段的的任务是:把中间代码(或经优化处理之后)变换成特定机器上的低级语言代码。这阶段实现了最后的翻译,它的工作有赖于硬件系统结构和机器指令含义。这阶段工作非常复杂,涉及到硬件系统功能部件的运用,机器指令的选择,各种数据类型变量的存储空间分配,以及寄存器和后援寄存器的调度,等等。如何产生出足以充分发挥硬件效率的目标代码是一件非常不容易的事情。

    目标代码的形式可以是绝对指令代码或可重定位的指令代码或汇编指令代码。如目标代码是绝对指令代码,则这种目标代码可立即执行。如果目标代码是汇编指令代码,则需汇编器汇编之后才能运行。必须指出,现代多数实用编译程序所产生的目标代码都是一种可重定位的指令代码。这种目标代码在运行前必须借助于一个连接装配程序把各个目标模块(包括系统提供的库模块)连接在一起,确定程序变量(或常数)在主存中的位置,装入内存中指定的起始地址,使之成为一个可以运行的绝对指令代码程序。

    上述编译过程的五个阶段是一种典型的分法。事实上,并非所有编译程序都分成这五阶段。有些编译程序对优化没有什么要求,优化阶段就可省去。在某些情况下,为了加快编译速度,中间代码生成阶段也可以去掉。有些最简单的编译程序是在语法分析的同时产生目标代码。但是,多数实用编译程序的工作过程大致都像上面所说的那五个阶段。

    编译程序的结构

    编译程序总框

    上述编译过程的五个阶段是编译程序工作时的动态特征。编译程序的结构可以按照这五阶段的任务分模块进行设计。

    词法分析器,又称扫描器,输入源程序,进行词法分析,输出单词符号。

    语法分析器,简称分析器,对单词符号串进行语法分析(根据语法规则进行推导或归约),识别出各类语法单位,最终判断输入串是否构成语法上正确的“程序”。

    语义分析及中间代码产生器,按照语义规则对语法分析器归约出(或推导出)的语法单位进行语义分析并把它们翻译成一定形式的中间代码。有的编译程序在识别出各类语法单位后,构造并输出一棵表示语法结构的语法树,然后,根据语法树进行语义分析和中间代码产生。还有许多编译程序在识别出语法单位后并不真正构造语法树,而是调用相应的语义子程序。在这种编译程序中,扫描器、分析器和中间代码生成器三者并非是截然分开的,而是相互穿插的。

    优化器,对中间代码进行优化处理。

    目标代码生成器,把中间代码翻译成目标程序。

    除了上述五个功能模块外,一个完整的编译程序还应包括“表格管理”和“出错处理”两部分。

    表格与表格管理

    编译程序在工作过程中需要保持一系列的表格,以登记源程序的各类信息和编译各阶段的进展状况。合理地设计和使用表格是编译程序构造的一个重要问题。在编译程序使用的表格中,最重要的是符号表。它用来登记源程序中出现的每个名字以及名字的各种属性,例如,一个名字是常量名、变量名,还是过程名等等;如果是变量名,它的类型是什么、所占内存是多大、地址是什么等等。通常,编译程序在处理到名字的定义性出现时,要把名字的各种属性填入到符号表中;当处理到名字的使用性出现时,要对名字的属性进行查证。

    当扫描器识别出一个名字(标识符)后,它把该名字填入到符号表中。但这时不能完全确定名字的属性,它的各种属性要在后续的各阶段才能填入。例如,名字的类型等要在语义分析时才能确定,而名字的地址可能要到目标代码生成才能确定。

    由此可见,编译各阶段都涉及到构造、查找、或更新有关的表格。

    出错处理

    一个编译程序不仅应能对书写正确的程序进行翻译,而且应能对出现在源程序中的错误进行处理。如果源程序有错误,编译程序应设法发现错误,把有关错误信息报告给用户。这部分工作是由专门的一组程序(叫做出错处理程序)完成的。一个好的编译程序应能最大限度地发现源程序中的各种错误,准确地指出错误的性质和发生错误的地点,并且能将错误所造成的影响限制在尽可能小的范围内,使得源程序的其余部分能继续被编译下去,以便进一步发现其他可能的错误。如果不仅能够发现错误,而且还能自动校正错误,那当然就更好了。但是,自动校正错误的代价是非常高的。

    编译过程的每一阶段都可能检测出错误,其中,绝大多数错误可以在编译的前三阶段检测出来。源程序中的错误通常分为语法错误和语义错误两大类语法错误是指源程序中不符合语法(或词法)规则的错误,它们可在词法分析或语法分析时检测出来。例如,词法分析阶段能够检测出“非法字符”之类的错误;语法分析阶段能够检测出诸如“括号不匹配”、“缺少 ;”之类的错误。语义错误是指源程序中不符合语义规则的错误,这些错误一般在语义分析时检测出来,有的语义错误要在运行时才能检测出来。语义错误通常包括:说明错误、作用域错误、类型不一致等等。

    遍pass

    在前面己经介绍了编译过程的五个阶段仅仅是逻辑功能上的一种划分。具体实现时,受不同源语言、设计要求、使用对象和计算机条件(如主存容量)的限制,往往将编译程序组织为若干遍(pass)。所谓“遍”就是对源程序或源程序的中间结果从头到尾扫描一次,并作有关的加工处理,生成新的中间结果或目标程序。通常,每遍的工作由从外存上获得的前一遍的中间结果开始(对于第一遍而言,从外存上获得源程序),完成它所含的有关工作之后,再把结果记录于外存。既可以将几个不同阶段合为一遍,也可以把一个阶段的工作分为若干遍。例如,词法分析这一阶段可以单独作为一遍,但更多的时候是把它与语法分析合并为一遍;为了便于处理,语法分析和语义分析及中间代码生成又常常合为一遍。在优化要求很高时,往往还可把优化阶段分为若干遍来实现。

    当一遍中包含若干阶段时,各阶段的工作是穿插进行的。例如,我们可以把词法分析、语法分析及语义分析与中间代码生成这三阶段安排成一遍。这时,语法分析器处于核心位置,当它在识别语法结构而需要下一单词符号时,它就调用词法分析器,一旦识别出一个语法单位时,它就调用中间代码生成器,完成相应的语义分析并产生相应的中间代码。

    一个编译程序究竟应分成几遍,如何划分,是与源语言、设计要求、硬件设备等诸因素有关的,因此难于统一划定。遍数多一点有个好处,即整个编译程序的逻辑结构可能清晰一点。但遍数多势必增加输入/输出所消耗的时间。因此,在主存可能的前提下,一般还是遍数尽可能少一点为好。应当注意的是,并不是每种语言都可以用单遍编译程序实现。

    编译前端与后端

    概念上,我们有时把编译程序划分为编译前端和编译后端前端主要由与源语言有关但与目标机无关的那些部分组成。这些部分通常包括词法分析、语法分析、语义分析及中间代码生成,有的代码优化工作也可包括在前端。后端包括编译程序中与目标机有关的那些部分,如与目标机有关的代码优化和目标代码生成等。通常,后端不依赖于源语言而仅仅依赖于中间语言。

    可以取编译程序的前端,改写其后端以生成不同目标机上的相同语言的编译程序。如果后端的设计是经过精心考虑的,那么后端的改写将用不了太大工作量,这样就可实现编译程序的目标机改变。也可以设想将几种源语言编译成相同的中间语言,然后为不同的前端配上相同的后端,这样就可为同一台机器生成不同语言的编译程序。然而,由于不同语言存在某些微妙的区别,因此在这方面所取得的成果还非常有限。

    为了实现编译程序可改变目标机,通常需要有一种定义良好的中间语言支持。例如,在著名的Ada程序设计环境APSE中,使用的是一种称为Diana的树形结构的中间语言。一个Ada源程序通过前端编译转换为Diana中间代码,由编译后端把Diana中间代码转换为目标代码。编译前端与不同的编译后端以Diana为界面,实现编译程序的目标机改变。

    又如,在Java语言环境中,为了使编译后的程序从一个平台移到另一个平台执行,Java定义一种虚拟机代码—Bytecode。只要实际使用的操作平台上实现了执行Bytecode的Java解释器,这个操作平台就可以执行各种Java程序。这就是所谓Java语言的操作平台无关性。

    编译程序与程序设计环境

    编译程序无疑是实现高级语言的一个最重要的工具。但支持程序设计人员进行程序开发通常还需要一些其他的工具;如编辑程序、连接程序,调试工具等等。编译程序与这些程序设计工具一起构成所谓的程序设计环境。

    在高级语言发展的早期,这些程序设计工具往往是独立的,缺乏整体性,而且也缺乏对软件开发全生命周期的支持 。随着软件技术的不断发展,现在人们越来越倾向于构造集成化的程序设计环境。一个集成化的程序设计环境的特点是,它将相互独立的程序设计工具集成起来,以便为程序员提供完整的、一体化的支持,从而进一步提高程序开发效率,改善程序质量。

    在一个好的集成化程序设计环境中,不仅包含丰富的程序设计工具,而且还支持程序设计方法学,支持程序开发的全生命周期。有代表性的集成化程序设计环境有Ada语言程序设计环境APSE、LISP语言程序设计环境INTERLISP等。广大读者所熟悉的Turbo Pascal、Turbo C、Visual C++等语言环境也都可认为是集成化的程序设计环境。

    在一个程序设计环境中,编译程序起着中心的作用。连接程序、调试程序、程序分析等工具的工作直接依赖于编译程序所产生的结果,而其他工具的构造也常常要用到编译的原理、方法和技术

    编译程序的生成

    以前人们构造编译程序大多是用机器语言或汇编语言作工具的。为了充分发挥各种不同硬件系统的效率,为了满足各种不同的具体要求,现在许多人仍然采用这种工具来构造编译程序(或编译程序的“核心”部分)。但是,越来越多的人已经使用高级语言作工具来编译程序。因为,这样可以大大节省程序设计时间,而且所构造出来的编译程序易于阅读、维护和移植。

    如果A机器上已有一个用A机器代码实现的某高级语言L1的编译程序,则我们可以用L1语言编写另一种高级L2的编译程序,把写好的L2编译程序经过L1编译程序编译后就可得到A机器代码实现的L2编译程序。

    采用一种所谓的“移植”方法,我们可以利用A机器上已有的高级语言L编写一个能够在B机器上运行的高级语言L的编译程序。做法是,先用L语言编写出在A机器上运行的产生B机器代码的L编译程序源程序,然后把该源程序经过A机器上的L编译程序编译后得到能在A机器上运行的产生B机器代码的编译程序,用这个编译程序再一次编译上述编译程序源程序就得到了能在B机器上运行的产生B机器代码的编译程序。

    还可以采用“自编译方式”产生编译程序方法是,先对语言的核心部分构造一个小小的编译程序(可用低级语言实现),再以它为工具构造一个能够编译更多语言成分的较大编译程序。如此扩展下去,就像滚雪球一样,越滚越大,最后形成人们所期望的整个编译程序。这种通过一系列自展途径而形成编译程序的过程叫做自编译过程。

    现在人们已建立了多种编制部分编译程序或整个编译程序的有效工具。有些能用于自动产生扫描器(如LEX),有些可用于自动产生语法分析器(如YACC),有些甚至可用来自动产生整个的编译程序。这些构造编译程序的工具称为编译程序-编译程序、编译程序产生器或翻译程序书写系统,它们是按对源程序和目标语言(或机器)的形式描述(作为输入数据)而自动产生编译程序的。

    参考文献及资源

    1. 陈火旺等,程序设计语言编译原理.国防工业出版社,2000年
    2. Compilers Principles, Techniques and Tools,A.V. Aho,Addison-Wesley,1986年
    3. Donald E. Knuth. The Art of Computer Programming, Volume 1/Fundamental Algorithms. Addison_Wesley, Reading, Massachusetts, Second Edition, 1973.
    4. Donald E. Knuth. The Art of Computer Programming, Volume 2/Seminumberical Algorithms. Addison_Wesley, Reading, Massachusetts, Second Edition, 1981.
    5. 郭浩志. PASCAL语言结构程序设计. 国防科技大学出版社,1988.
    6. 郭浩志. 程序设计语言概论. 国防科技大学出版社,1989.
    7. 易文韬,陈颖平. Java手册. 科学出版社,1997.

    关于编译原理博文更多讨论与交流,敬请关注本博客新浪微博songzi_tea.

    展开全文
  • 编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语言(低级语言),则此翻译程序称为编译程序 解释程序:解释,执行高级语言源程序的程序 源程序:源语言编写的程序为源程序,一般为用高级...
    大家好,我是小黄鸭,平时作业,记录一下,方便考试复习
    

    第一章

    1. 第一题 解释下列术语
      1. 翻译程序:翻译程序是一种把源语言书写的程序翻译成另一种语言书写的程序,而且后者和前者在逻辑上是等价的
      2. 编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语言(低级语言),则此翻译程序称为编译程序
      3. 解释程序:解释,执行高级语言源程序的程序
      4. 源程序:源语言编写的程序为源程序,一般为用高级语言编写的程序
      5. 目标程序:指用低级语言(机器语言或汇编语言)编写的程序
      6. 遍:是对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程
      7. 前端:主要由与源语言有关,但与目标机无关的那些部分组成,这些部分通常包括词法分析、语法分析、语义分析与中间代码生产,有的代码优化工作可以包括在前端
      8. 后端:包括编译程序中与目标机有关的那部分,如与目标机有关的代码优化和目标代码生产等。后端不依赖于源语言,仅依赖于中间语言。
    2. 第四题 编译程序有哪些主要构成成分?各自的主要功能是什么?
      一个典型的编译程序通常包括7个组成部分(有些地方也写8个,区分不同而已),他们是词法分析程序、语法分析程序、语义分析与中间代码生成程序、代码优化程序、目标代码生成程序、表格管理程序和错误处理程序。
      1. 词法分析程序:对输入的源程序进行从左到右的扫描和分解,识别出一个个单词,并以单词的内部表示形式输出。同时对源程序做一些简单的处理,如过滤空格、去掉注释、报告错误。
      2. 语法分析程序:在词法分析的基础上,将单词符号组成各类语法短语,如程序、语句、表达式等,通过分析确定整个输入串是否构成语法上正确的程序。这种语法短语,亦可称为语法单位、语法范畴,可以表示成语法树。
      3. 语义分析与中间代码生产:对语法分析所识别出的各类语法短语,分析其含义,进行初步的翻译(产生中间代码)。这一阶段有两项工作:首先对每个语法短语进行静态语义审查,如标识符是否定义,类型是否匹配等。若无语义错误,则进行中间代码的翻译。
      4. 代码优化程序:对已产生的中间代码进行加工变换,使生成的目标代码更为高效。
      5. 目标代码生成程序:把中间代码(经过优化的中间代码)变换成特定机器上的低级语言代码
      6. 表格管理程序:负责建立、填写和查找等一系列表格管理工作。
      7. 错误处理程序:处理和校正源程序中存在的词法、语法和语义错误。

    第二章

    习题1

    第三章

    1. 第四题
      1. 上下文无关文法:若文法 G=(Vn,Vt,P,S)中的每一条规则的形式为 A->β,其中A∈Vnβ∈(Vn U Vt)* ,则称G是2型文法,即上下文无关文法。
      2. 推导:如果 A-> γ 是产生式,α和β是文法的任意符号串,αAβ -> αγβ 称为直接推导
      3. 最左推导:如果在整个推导中,每一步都是替换句型中最左边的非终结符,这样的推导称为最左推导。
      4. 最右推导:即在推导的每一步都替换最右边的非终结符,这样的推导称为最右推导。
      5. 句型:若S是文法G的开始符号,从开始符号S出发推导出的符号串称为文法G的一个句型。
      6. 句子:若X是文法G中的一个句型,且X∈Vt*,则称X是文法G的一个句子。
      7. 语言:把文法G产生的所有句子的集合称为G产生的语言,即为L(G)
      8. 文法等价:如果文法G1与文法G2产生的语言是相同的,即L(G1)=L(G2),则称这两个文法是等价的。
      9. 语法树:用一课树来表示句型的推导称为语法树。
      10. 二义文法:如果存在某个句型对应两颗或量颗以上的语法树,则称这个文法为而二义文法。
      11. LL(1)文法:对文法G的句子进行确定的自顶向下语法分析的充分必要条件是,G的任意两个具有相同左部的,产生式A—>α|β 满足下列条件:
        1. 如果α、β均不能推导出ε,则 FIRST(α) ∩ FIRST(β) = ∅。
        2. α 和 β 至多有一个能推导出 ε。
        3. 如果 β *═> ε,则 FIRST(α) ∩ FOLLOW(A) = ∅。
      12. 规约:设置一个栈,从输入符号串(指的是从语法分析器送来的单词符号)出发,将输入符号逐个移入栈中,边移入边分析,一旦栈顶形成某个产生式的右部时,就用该产生式左部的非终结符代替,称为规约。
      13. 规范规约:在规约过程中始终对句柄进行规约而形成的序列称为规范规约,也称为最左规约。
      14. 句柄:位于一个句型的最左直接短语称为该句型的句柄
      15. 短语: 若有 S⇒*xUy⇒+xuy,则u称为句型xuy相对于U的短语。

    第四章

    题目2

    第五章

    1. 第一题 解释下列术语
      1. 属性:代表与文法符号相关的信息,和变量一样,可以进行计算和传递。
      2. 属性文法:包含一个上下文无关文法和一系列的语义规则,并为每个文法符号(终结符和非终结符)配备若干相关的“值”(属性)
      3. 继承属性:在一个属性文法中,每个产生式A->α都有一个形如b=f(c1,c2...,ck)的语义规则集,如果b是产生式右边某个文法符号X的一个属性,且c1,c2...,ck是A或产生式右边任何文法符号的属性,则称bA继承属性
      4. 综合属性:在一个属性文法中,每个产生式A->α都有一个形如b=f(c1,c2...,ck)的语义规则集,如果bA的一个属性,且c1,c2...,ck是产生式右边文法符号α的属性,或者是A的其他属性,则称bA综合属性
      5. 语义子程序:把某一种程序设计语言表示的程序转换为逻辑上等价的用另一种程序设计语言表示的程序的程序
      6. 语法制导的翻译:如果遍历语法树的操作和建立语法树的操作同时进行,成为语法制导的翻译方法。
      7. 翻译模式:给出使用语义规则进行计算的次序,把实现细节表示出来。

    习题1
    习题2
    习题3
    习题4
    First和Follow的求解1
    习题5
    复习题集(蛮重要)
    习题6

    展开全文
  • 程序生成之编译、链接、加载浅析

    千次阅读 2017-04-28 11:02:35
    程序生成之编译、链接、加载浅析
  • PB应用程序编译发布技术研究

    千次阅读 2006-09-05 17:06:00
    摘要:本文主要讨论基于Windows平台的PowerBuilder 8.0应用程序编译发布关键问题和解决方法,包括执行文件的建立、资源文件的创建、安装程序的建立及添加数据源和SQL anywhere 数据库移动处理等问题。关键字:软件...
  • 编译原理——语言处理程序

    千次阅读 热门讨论 2014-05-03 21:17:41
    程序语言是为了书写计算机程序而人为设计的符号,用于对计算过程进行描述、组织和推导,程序语言分为低级语言和高级语言,低级语言包括计算机硬件能识别的由0、1组成的机器指令语言和用符号组成的指令语言汇编集合的...
  • 编译原理第三版答案

    万次阅读 多人点赞 2018-12-19 21:21:23
    答:编译程序主要由以下几个部分组成:词法分析、语法分析、语义分析、中间代码生成、中间代码优化、目标代码生成、错误处理、表格管理。 2. 实现编译程序的主要方法有哪些? 答:主要有:转换法、移植法、自展法...
  • PB8.0应用程序编译发布技术研究

    千次阅读 2005-02-06 12:50:00
    摘要:本文主要讨论基于Windows平台的PowerBuilder 8.0应用程序编译发布关键问题和解决方法,包括执行文件的建立、资源文件的创建、安装程序的建立及添加数据源和SQL anywhere 数据库移动处理等问题。关键字:软件...
  • 如何用csc.exe来编译C#程序

    千次阅读 2006-11-23 09:58:00
    C#是微软公司推出的新一代程序开发语言,Visual C#是微软公司.Net FrameWork框架中的一个重要的组成部分,也是微软公司向程序员极力推荐一个新的程序开发平台。和以往的开发语言相比较,他有更强大功能,更高的...
  • 编译原理习题答案

    万次阅读 2007-11-22 16:41:00
    第一章 编译程序概述1.1 什么是编译程序 编译程序是现代计算机系统的基本组成部分之一,而且多数计算机系统都含有不止一个高级语言的编译程序。对有些高级语言甚至配置了几个不同性能的编译程序。1.2编译过程概述和...
  • 编译原理

    千次阅读 2012-08-24 08:56:18
    编译程序的主要功能是将源程序翻译成等价的目标程序,这个翻译过程(也称编译过程)十分复杂,所以它一般首先分析源程序,然后综合成目标程序。编译程序在分析阶段检查源程序的正确性后,再将其分解为若干基本成分。...
  • Linux系统一般有4个主要部分: 内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序、管理文件并使用系统。部分层次结构如图1-1所示。 ...
  • 编译原理知识点

    千次阅读 多人点赞 2019-05-16 09:15:52
    1. 编译程序(编译器):先将源程序翻译成汇编语言程序或机器语言程序(称为目标程序),然后再执行它。 2. 解释程序(解释器):按解释方式进行翻译的翻译程序称为解释程序。解释程序的主要优点是便于对源程序进行...
  • 1.构造编译程序应掌握 (D ) 。 a. 源程序 b. 目标语言 c. 编译方法 d. 以上三项都是 2.编译程序绝大多数时间花在 (D) 上。 a. 出错处理 b. 词法分析 c. 目标代码生成 d. 表格管理 3.DFA M(见图1-1)接受的字集为...
  • 编译程序的前端:它由这样一些阶段组成:这些阶段的工作主要依赖于源语言而与目标机无关。通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶段,某些优化工作也可在前端做,也包括与...
  • 编译原理:习题

    千次阅读 2019-06-12 13:07:59
    Q1. 下列哪种语言是依赖于机器的? (10 分)( D ) A. 自然语言 B. 高级语言 C....D....Q2. 编译程序是对( )程序进行翻译?...A....B....C....D....Q3. 编译程序的工作过程按照先后顺序依次划分为_词法分析__,语法分析_,语...
  • 编译原理答案--第一章

    千次阅读 2014-12-02 09:42:34
    《编译原理》课后习题答案第一章第1章引论第1题解释下列术语:(1)编译程序(2)源程序(3)目标程序(4)编译程序的前端(5)后端(6)遍答案:(1) 编译程序:如果源语言为高级语言,目标语言为某台计算机上的...
  • 程序设计语言及其文法1 文法:G:S→xSx | y所识别的语言是( )。 2 给定文法A→bA|ca,为该文法句子的是( )。A. bbaB. cabC. bcaD. Cba 3 设有文法G[S]:S-&gt;S1|S0|Sa|Sc|a|b|c,下列符号串中是该文法的句子...
  • 编译原理 练习题

    千次阅读 多人点赞 2019-07-03 13:11:38
    1.编译程序的工作过程一般可以划分为 词法分析 , 语法分析 , 中间代码生成 , 代码优化 (可省) , 目标代码生成 等几个基本阶段。 2.若源程序是用高级语言编写的,目标程序是 机器语言程序或汇编程序 ,则其翻译...
  • 使用JDK命令行编译、运行Java程序 一般都是通过IDE(如Eclipse、Intellij Idea,MyEclipse)来开发,调试java项目。但作为学习Java初学者,一上来就用这些IDE,可能会被分神,原因是学习IDE的使用也需要相当的付出...
  • 1 文法:G:S→xSx | y所识别的语言是( )。 2 给定文法A→bA|ca,...S1|S0|Sa|Sc|a|b|c,下列符号串中是该文法的句子有( )。 A. ab0 B. a0b01 C. a0b0a D. bc10 4 文法G产生的( )的全体是该文法描述的语言。 A. 句...
  • 编译原理 词法分析

    千次阅读 2016-04-20 17:45:50
    编译原理词法分析词法分析的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用于语法分析。 1.正则表达式对给定的字符集∑={c1,c2,...,cn},归纳定义: 1.空串ε是正则表达式 2.对于任意c∈∑,...
  • 编译原理:课程重点

    千次阅读 2019-01-06 08:51:22
    一个典型的编译程序通常由哪些部分组成? 画出编译程序的总体结构图。 各部分的主要功能是什么? 每个阶段将源程序从一种表示转换成另一种表示: 词法分析器:字符流-&amp;amp;amp;gt;单词流 语法分析...
  • 编译原理习题(含答案)

    千次阅读 2020-05-07 08:12:03
    语法分析_1 1 如果文法G是无二义的,则它的任何句子α( )。 A. 最左推导和最右推导对应... SLR(1)分析法 10 编译程序的语法分析器必须输出的信息是( )。 A. 语法规则信息 B. 语法错误信息 C. 语法分析过程 D. 语句序列
  • 慕课编译原理(习题集)

    千次阅读 2020-06-15 21:54:37
    习题汇总0 目录1 引论1.1 什么是编译程序1.2 为什么要学习编译原理1.3 编译过程1.4 编译程序的结构1.5 编译程序的生成1.6 小结1.7 课后作业2 高级程序设计语言概论2.1 常用的高级程序设计语言2.2 程序设计语言的定义...
  • 编译原理习题复习

    千次阅读 2019-09-05 12:34:03
    1、编写一个计算机高级语言的源程序后,到正式上机运行之前,一般要经过编辑、编译、连接、运行这几步。...5、编译程序前三个阶段完成的工作是:词法分析、语法分析、语义分析和中间代码生成 6、文法分为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,157
精华内容 16,062
关键字:

下列不是编译程序组成部分