您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > OpenMP (3)
36错误处理由于编程人员不可避免的会写出有错误的代码,一个可用的编译器必须能够发现大多数常见错误,并能准确地报告出错误在源代码中的位置,否则就没有使用价值。由于编译系统的各个部分都可能需要程序来诊断问题,所以错误处理代码是广泛分布于编译器的各个角落,在需要的地方进行检查和诊断并报告错误所在。3.2.2工作流程虽然典型编译器都有八个功能部件,但是图3.5所表示的只是它们的逻辑组织方式,并不代码执行上的先后顺序。在一个特定的实现中,多个步骤的活动可以被组合成一“遍”(Pass)或称为一“趟”的执行单位,每遍读入一个输入文件并产生一个输出文件。比如前端步骤中的词法分析、语法分析、语义分析,以及中间代码生成可以被组合成一个前端遍;代码优化可以作为一个可选的遍;最后是为特定目标机生成代码的后端遍。3.3编译优化采用标准C代码作为输出的源代码级别的OpenMP编译虽然具有良好的可移植性和平台无关性,但是这种方案将OpenMP编译与后端的优化编译器分割开来,因此限制了线程级并行与指令集并行性开发之间的交互,使得很多优化方法无法实施。如果采用另一种实现方式,将OpenMP的翻译作为后端工作的一部分(牺牲了可移植性),此时由于OpenMP翻译、优化与面向指令级并行性的编译和优化可以共同在中间表示的层面上开展,可以灵活安排优化的相对顺序,也可以方便地在各种不同的分析、优化过程之间传递信息,因此可以获得比源代码级翻译更多的优化机会。但是即便采用源代码级的翻译,也可以在并行域的合并、冗余制导指令消除以及针对变量的数据属性进行优化。另外在与运行库方面,也有许多优化技术,比如采用开销更小的线程库、针对NUMA架构利用局部性原理对数据分配和线程与处理器和的绑定等等技术来提高性能。本书作为入门级参考材料,并没有对性能优化进行讨论,需要读者自行阅读相关论文和书籍。3.4小结本章介绍了OpenMP编译的狭义定义,简单分析了相应的编译器的基本构件:词法分析模块、语法分析模块、语义分析模块、中间代码生成、代码优化模块、目标代码生成以及符号表管理和错误检查。其中词法分析中要注意OpenMP与C语言共用关键字的区分,语法分析程序需要能在C语法基础之上识别OpenMP制导指令的语法,中间代码选取AST以便保留源代码的语法层次结构,目标代码生成中需要翻译OpenMP的并行语义。通过简单的介绍,在复习编译原理的基础之上初步了解OpenMP编译所涉及的几个问题,形成初步的概念。374第4章词法与语法分析在编译器的前端产生出中间代码的第一遍(Pass)步骤中,OpenMP编译器和其他编译器很相似,就是通过词法分析和语法分析建立起抽象语法树AST中间代码表示,词法分析和语法分析的原理可以参考编译原理的教材,本章只讨论如何利用工具来实现OpenMP的词法分析和语法分析。我们将基于两个开源工具Lex和Yacc来讨论,而该工具的原理以及可以适用的文法类型可以参考编译原理的书籍。在进行语法分析的同时可以编写相应的语义动作函数从而建立起AST(其中又涉及AST树节点的设计问题),这些问题将在第5章讨论。Lex和Yacc是UNIX类操作系统中两个非常重要的、功能强大的工具。Lex代表LexicalAnalyzar,Yacc代表YetAnotherCompilerCompiler。如果能熟练掌握Lex和Yacc,利用它们强大的功能可以非常容易地创建FORTRAN、C或其他编程语言的编译器。如果没有这样的工具,在开发程序的过程中遇到文本解析的问题时,例如解析C语言源程序、编写脚本引擎等等,就需要自己手动用C或者C++直接编写解析程序,这对于简单格式的文本信息来说,不会是什么问题,但是对于稍微复杂一点的文本信息的解析来说,手工编写解析器将会是一件漫长痛苦而容易出错的事情。如果可以通过某种格式的文件来描述其词法或语法规则,再由工具软件来自动生成分析工具,那么将极大提高开发效率减少编程错误。Lex指词法扫描器,Yacc指语法分析器,这是通用的说法。但是具体的实现将会有所不同,GNU的Lex就是Flex,GNU的Yacc就是Bison。为了统一,所以在后面的章节就只会用Lex来表示词法扫描器,用Yacc来表示语法分析器。4.1Lex工具本节要讨论编写某种的语言的编译器所用到的第一种工具——Lex,内容包括正则表达式、声明、匹配模式、变量,然后在后续小节继续讨论Yacc语法和解析器代码、以及如何把Lex和Yacc结合起来。Lex是一种生成词法扫描器的工具,扫描器是一种识别文本中的词汇模式的程序。这些词汇模式(或者正则表达式)定义在一种特殊的句子结构中。当Lex生成的扫描器在扫描接收到文件或文本形式的输入时,它试图将文本与正则表达式进行匹配。它一次读入一个输入字符,直到找到一个匹配的模式,Lex就执行相关的动作(可能包括返回一个标记)。另一方面,如果没有可以匹配的正则表达式,将会停止进一步的处理,Lex将显示一个错误消息。因此需要对某种语言的待编译的源代码进行词法分析时,开发者首先可以用一个名为*.lex的Lex文件(Lex文件具有.lex或.l的扩展名)来描述其词法规则。这个Lex文件通过Lex公用程序(称Lex编译器)来处理,并生成C的输出文件。输出的C文件可以被编译为词法分析器的可执行版本。所以在词法分析中用户的主要工作就是编写Lex文件将词法规则描述清38楚,剩下的工作由Lex编译器完成,直到提供出一个可用的词法分析器的C文件。OMPi利用Lex来完成C和OpenMP的词法分析,其工作流程如图4.1所示。图4.1OMPi中Lex的作用示意图OMPi的词法规则写在scanner.l文件(Lex文件)中,经过Lex的编译输出为scanner.c文件,由于OMPi没有将scanner.c设计成独立运行的程序,因此它是和其他源代码一起工作的,由语法分析程序parser.c所调用。词法扫描器和语法分析器的功能集成在应用程序ompi中,ompi不仅完成词法分析还完成语法分析等其他编译功能,图4.1例子ompi将输入OpenMP/C源文件Myopenmp.c作为输入直到输出编译后的目标代码Myopenmp_comp.c,完成源代码到源代码的转换。也就是说它的词法分析输出结果对用户并不可见,如果需要输出结果可见,可以另行输出。4.1.1Lex的正则表达式由于开发者最重要的工作就是描述好词法规则,所以先来讨论这种统一的描述方法。对词法规则的描述正是通过表达式规则来说明的,所使用的正则表达式(RegularExpression)是一种使用元语言的模式描述。表达式由符号组成,符号一般是字符和数字,但是Lex中还有一些具有特殊含义的其他标记。表4.1给出了Lex的一些表达式规则。表4.1Lex的正则表达式字符含义A-Z,0-9,a-z构成了部分模式的字符和数字。.匹配任意字符,除了\n。-用来指定范围。例如:A-Z指从A到Z之间的所有字符。[]一个字符集合。匹配括号内的任意字符。如果第一个字符是^那么它表示否定模式。例如:[abC]匹配a,b,和C中的任何一个。*匹配0个或者多个上述的模式。+匹配1个或者多个上述模式。?匹配0个或1个上述模式。$作为模式的最后一个字符匹配一行的结尾。词法规则描述文件scanner.l(词法分析器规格说明)Lex编译器分析器程序scanner.c……scanner.cparser.c……C编译器可执行文件ompiMyopenmp.c(OpenMP/C源程序)ompiMyopenmp_omp.c39{}指出一个模式可能出现的次数。例如:A{1,3}表示A可能出现1次或3次。\用来转义元字符。同样用来覆盖字符在此表中定义的特殊意义,只取字符的本意。^否定。|表达式间的逻辑或。一些符号字符的字面含义。/向前匹配。如果在匹配的模版中的“/”后跟有后续表达式,只匹配模版中“/”前面的部分。如:如果输入A01,那么在模版A0/1中的A0是匹配的。()将一系列正则表达式分组。扫描器的开发者可以利用上述规则来描述目标语言的词法规则,表4.2给出了几个描述词法模式的例子。表4.2正则表达式举例正则表达式含义joke[rs]匹配jokes或joker。A{1,2}shis+匹配AAshis,Ashis,AAshiss,Ashis等。A([b-e])?匹配在A出现位置后跟随的从b到e的所有字符中的0个或1个。[\t]*#[\t]*pragma[\t]+omp[\t]+匹配形如#pragmaomp的串另外,还可以在Lex文件中声明标记,Lex中的标记声明类似C中的宏定义,方便用户编程使用。每个标记都有一个相关的表达式。表4.3中给出了标记和表达式的例子。在后面我们以一个字数统计的程序为例说明Lex编程的时候,其中就要用到这里讲到的如何声明标记的方法。表4.3标记声明举例标记相关表达式含义number([0-9])+数字,1个或多个数字chars[A-Za-z]字符,任意字母字符blank空格,一个空格word(chars)+字,1个或多个charsvariable(字符)+(数字)*(字符)*(数字)*变量名从表4.3中可以知道此处定义了一个名为number的标记,它代表了“数字”表达式——由1个或多个数字字符组成。同理也可以知道chars标记代表有字母字符所组成的字符串。4.1.2Lex使用方法使用Lex开发扫描器的编程过程可以分为三步:首先在某个文件中以Lex可以理解的格式指定模式及相关的动作;然后在这个文件上运行Lex,生成扫描器的C代码;最后编译和链接C代码,生成可执行的扫描器。如果扫描器是用Yacc开发的解析器的一部分,只需要进行第一步和第二步。40现在需要看一看Lex可以理解的程序文件格式,一个Lex程序根据其作用可以分为三个段,形式如下:声明部分%%规则部分%%辅助函数这些段以%%来分界,第一段是声明段,有C和Lex的全局声明;第二段包括模式和动作(C代码);第三段是补充的C函数。如果作为独立扫描器使用,那么第三段中需要有main()函数。下面将利用Lex工具来生成一个用于字数统计的程序,并以此来分析Lex程序不同段的构成。C和Lex的全局声明这一段中可以有C变量声明。以字数统计程序为例,需要声明一个整型变量,来保存程序统计出来的字数,后面还将进行Lex的标记声明。字数统计程序的声明段:1.%{2.intwordCount=0;2字数统计值3.%}4.chars[A-za-z\_\'\.\]以下是5个标记声明5.numbers([0-9])+6.delim[\n\t]7.whitespace{delim}+8.words{chars}+9.%%C的声明部分起始于%{符号,终止于%}符号,其间可以是包括include语句、声明语句在内的C语句。此例子中声明了一个初始值为0的用于字数统计值的整型变量wordCount。Lex的标记则不需要额外标注,此处声明了5种标记,都使用正则表达式,它们将在第二部分中被用到。第一个chars标记是字母、下划线、“.”和引号,第二种是数字组成的字符串,第三种是分割符号,第四种是一个或多个分割符号、第五种是单词(1个或多个chars)。两个百分号标记指出了Lex程序中这一段的结束和第二段的开始。Lex的模式匹配规则Lex描述所要匹配的标记的规则,它出现在第二段。我们将使用C来定义标记匹配后的动作。下面是字数统计程序中的Lex标记匹配的规则。10.{words}{wordCount++;/*increasethewordcountbyone*/}11.{whitespace}{/*donothing*/}12.{numbers}{/*onemaywanttoaddsomeprocessinghere*/}13.%%2书中的代码中出现的中文,是作者所加的注释。41这里有三个项,每个项说明一个匹配规则和相应的动作。这里的匹配规则是利用了第一部分定义的标记,因此都用{…}括起来,当然更常见的是直接用正则表达式描述匹配规则。每个项的匹配规则后的动作也用一个{…}括起来
本文标题:OpenMP (3)
链接地址:https://www.777doc.com/doc-5401040 .html