Skip to content

Latest commit

 

History

History

README.MD

Compilers Principles -- Alfred(赵建华等译)

大名鼎鼎的龙书,专门讲编译原理,编译器的设计和实现。

第1章 引论

编译器:将源语言程序翻译成目标语言程序
解释器:不翻译源程序,而是边解释边执行源程序
预处理器:把源程序聚合在一起,宏展开
汇编器:将汇编语言程序生成可重定位的机器码
链接器:解决外部内存地址问题,将多个目标文件和库文件链接到一起
加载器:把所有的可执行目标文件放到内存中执行

1.1.1:解释器是一条一条的解释执行源语言;编译器是把源代码整个编译成目标代码,执行时不在需要编译器,直接在支持目标代码的平台上运行
1.1.2:执行效率高;错误诊断更优
1.1.3:汇编语言更容易调试、检查bug。跨平台,可移植性强
1.1.4:C语言小巧灵活,通用性高,执行效率高
1.1.5:将汇编程序,翻译成机器指令

词法分析:
语法分析:
语义分析:
代码优化:
代码生成:

1.3.1:
	强制式的语言:C C++ Java
	声明式的语言:ML Haskell Prolog
	冯诺依曼式语言:Fortran C 
	面向对象的语言:C++ Java C# Smalltalk Ruby
	函数式的语言:ML Haskell scheme
	第三代语言:Fortran Gobol Lisp C C++ C# Java VB
	第四代语言:NOMAD SQL PostScript
	脚本语言:Awk JavaScript Perl PHP Python Ruby
	
高性能系统:并行性(指令级并行、处理器级并行)和内存层次结构(cache)
		
静态和动态:多态
作用域:public、private、protected
参数传递机制:传值、传引用、传名(类似宏)  
别名
1.6.1:w = 13, x = 11, y = 13, z = 11
1.6.2:w = 9, x = 7, y = 13, 11 
1.6.3:		w1: B1-B3-B4
			x1: B1-B2-B4
			y1: B1-B5
			z1: B1-B2-B5
			x2: B2-B3
			z2: B2
			w3: B3 
			x3: B3 
			w4: B4
			x4: B4 
			y5: B5
			z5: B5 
1.6.4:3\n 2\n 

第2章 语法制导翻译器

语法分析:上下文无关文法--->产生式
2.2.1:1.S-->SS*-->(S)S*-->(SS+)S*-->(aS+)S*-->aa+a*	S-->SS*-->(S)a*-->(SS+)a*-->(Sa+)a*-->aa+a*	
	2.				S
					|
			S		S 	*
			|		|
		S 	S 	+	a
		|	|
		a 	a 
	3.文法生成的语言是以a为基本运算因子(factor)的+和*运算符表达式的后缀形式
2.2.2:(1).(0)^n(1)^n 	(2).基于a的前缀表达式	(3).对称括号对的串	
	(4).ab个数相等的字符串	(5).以a为factor的字符串,有+、*、()等符号
2.2.3:1、2无二义		3、4、5有二义
2.2.4:(1).S-->SS+|SS-|SS*|SS/|a	(2).S-->S,id|id 	(3).S-->id,S|id 
		(4).expr-->expr+expr|expr-expr|expr*expr|expr/expr|id|digit
		(5).expr-->expr+expr|expr-expr|expr*expr|expr/expr|id|digit|+expr|-expr 
2.2.5:(11)=3,(1001)=9,(num 0)=(num)*2,(num num)=(num)*2*2+(11)|(num)*16+(1001)。显然文法生成的所有的数字,都可以被3整数.(x0=3, x1=9, fnx=2x|4x+3|16x+9)
		不能生成所有的:3,6,9,12,15,18,24,27
2.2.6: RomanNum --> Thousands Hundreds Tens Ones
		Ones --> LowOnes|IV|V LowOnes|IX
		LowOnes --> epsilon|I|II|III
		Tens --> LowTens|XL|L LowTens|XC
		LowTens --> epsilon|X|XX|XXX
		Hundreds --> LowHundreds|CD|D LowHundreds|CM
		LowHundreds -->epsilon|C|CC|CCC
		Thousands --> M Thousands|epsilon
语法分析树:语义动作,后序遍历
2.3.1:S-->S1+S2  S.t='+'||S1.t||S2.t
		S-->S1-S2  S.t='-'||S1.t||S2.t
2.3.2:S-->S1S2+  S.t=S1.t||'+'||S2.t
		S-->S1S2-  S.t=S1.t||'-'||S2.t
2.3.3: u -> u + u | pn | n
		p -> 1 | 10 | 100
		n -> 1 | 5 | 10 | 50 | 100 | 500 | 1000
		1 -> I
		5 -> V
		10 -> X
		50 -> L
		100 -> C
		500 -> M
		1000 -> D
2.3.4:2.3.3相反的过程 
2.3.5:S-->S1S2+  S.t='+'||S1.t||S2.t
		S-->S1S2-  S.t='-'||S1.t||S2.t
向前看符号、预测分析器
左递归、右递归
2.4.1:
(1)	void S(){
		switch(lookhead){
			case '+':
				match('+');
				S();
				S();
				break;
			case '-':
				match('-');
				S();
				S();
				break;
			case 'a':
				match('a');
				break;
			default:
				report("syntax error");
		}
	}
	
	void match(Terminal t){
		if (lookhead == t){
			lookhead = nextTerminal;
		}
		else{
			report("syntax error");
		}
	}
(2).	void S(){
			if (lookhead == '('){
				S();
				match('(');
				S();
				match(')');
				S();
			}
			// epsilon
		}
(3).	void S(){
			switch (lookhead){
				case '0':
					match('0');
					S();
					match('1');
					break;
				case '1':
					break;
				default:
					report("syntax error");
			}
		}
词法分析:忽略空白符和换行符、抽取token(数字和标识符)
2.6.1: 2.6.2:2.6.3: 
public Token scan() throws IOException, SyntaxException{    
	for( ; ; peek = (char)stream.read()){
		if(peek == ' ' || peek == '\t'){        
			continue;      	
		}
		else if(peek == '\n'){
			line = line + 1;      
		}
		else{        
			break;      
		}    
	}
	
	// handle comment    
	if(peek == '/'){      
		peek = (char) stream.read();      
		if(peek == '/'){        // single line comment        
			for( ; ; peek = (char)stream.read()){          
				if(peek == '\n'){
					break;          
				}        
			}      
		}
		else if(peek == '*'){        // block comment        
			char prevPeek = ' ';        
			for( ; ; prevPeek = peek, peek = (char)stream.read()){
				if(prevPeek == '*' && peek == '/'){            
					break;          
				}        
			}
		}
		else{
			throw new SyntaxException();      
		}    
	}   
	
	// handle relation sign    
	if("<=!>".indexOf(peek) > -1){      
		StringBuffer b = new StringBuffer();      
		b.append(peek);      \
		peek = (char)stream.read();      
		if(peek == '='){        
			b.append(peek);      
		}      
		return new Rel(b.toString());    
	}
	
	// handle number, no type sensitive    
	if(Character.isDigit(peek) || peek == '.'){      
		Boolean isDotExist = false;      
		StringBuffer b = new StringBuffer();      
		do{
			if(peek == '.'){          
				isDotExist = true;        
			}        
			b.append(peek);        
			peek = (char)stream.read();      
		} while (isDotExist == true ? Character.isDigit(peek) : Character.isDigit(peek) || peek == '.');      
		
		return new Num(new Float(b.toString()));    
	}      
	
	// handle word
	if(Character.isLetter(peek)){      
		StringBuffer b = new StringBuffer();      
		do{        
			b.append(peek);        
			peek = (char)stream.read();      
		} while (Character.isLetterOrDigit(peek));
		
		String s = b.toString();      
		Word w = words.get(s);      
		if(w == null){        
			w = new Word(Tag.ID, s);        
			words.put(s, w);      
		}      
		return w;    
	}      
	
	Token t = new Token(peek);    
	peek = ' ';    
	return t;  
} 		

中间代码:抽象语法树、三地址代码
类型检查、自动类型转换
2.8.1:	class For extends Stmt{
			Expr e1;
			Expr e2;
			Expr e3;
			Stmt s;
			
			public For(Expr expr1, Expr expr2, Expr expr3, Stmt stmt){
				e1 = expr1;
				e2 = expr2;
				e3 = expr3;
				s = stmt;
			}
			
			public void gen(){
				e1.gen();
				Label start = new Label();
				Label end = new Label();
				emit("ifFalse " + e2.rvalue().toString() + " goto " + end);
				s.gen();
				e3.gen();
				emit("goto " + start);
				emit(end + " ");
			}
		}
2.8.2: emit("isFalse " + E.rvalue().toString() + " goto " + after); 
		emit("ifNotEqual " + E.rvalue().toString() + " 0 goto " + after); 
	OR	emit("isNotEqualZero " + E.rvalue().toString() + " goto " + after);

第3章 词法分析

正则表达式 --> 不确定有穷状态自动机 --> 确定有穷状态自动机
词法分析器:读入源程序的字符串,将源程序中的字符组成一个个词素,生成对应的词法单元
3.1.1:
3.1.2:
正则表达式:* + ? [a-z]
3.3.1: 
	C:ASCII,标识符只能是(字母|下划线)开头的,(字母|下划线|数字)组成的字符串
	C++:Unicode,标识符只能是(字母|下划线)开头的,(字母|下划线|数字)组成的字符串
	C#:
	Fortran:
	Java:Unicode,标识符只能是(字母|下划线)开头的,(字母|下划线|数字)组成的字符串
	Lisp:
	SQL:
3.3.2: 
	(1).由a开头和结尾,中间有n个a或b构成的字符串
	(2).由b或者ab一起构成的字符串
	(3).ab构成的字符串,倒数第三位是a,其他位a|b 
	(4).有ab构成的字符串,有且仅有三个b
	(5).有偶数个a和偶数个b构成的字符串
3.3.3: n+1, n+1, n-1, (n-1)(n-2)/2+2, 2^n
3.3.4:(select) --> [sS][eE][lL][eE][cC][tT]
	大小写可以混合使用,说明大写字母和小写字母之间的关系是可选择的,但是必须选择其一。
3.3.5:
	(1).(a)+(e)+(i)+(o)+(u)+		uv=[b-df-hj-np-tv-z] uv*auv*euv*iuv*ouv*uuv*
	(2).(a)+(b)+...(y)+(z)+			a*b*c*....y*z*
3.3.6:
	(1).[a-jA-J]
	(2).[b-df-hj-np-tv-z]
	(3).[0-9a-f]
	(4).[,.!?]
3.3.7:
3.3.8:
3.3.9:
3.3.10:
3.3.11:
3.3.12:
状态转换图:词法分析
3.4.1: 
3.4.2:
3.4.3:
	(1).0 0 1 2 3 4 5 1 2
	(2).0 1 2 3 4 5
	(3).0 0 0 1 1 2 3
3.4.4: 
3.4.5: 
3.4.6:(1)是的,(2)不是的
3.4.7:
3.4.8:
3.4.9:
3.4.10:
3.4.11:
3.4.12:
词法分析器生成工具Lex
3.5.1: 
	(1).while {return(WHILE);}
3.5.2: 
3.5.3: 
3.5.4: 
3.5.5: 
NFA 和 DFA:词法分析器,实际模拟的都是DFA
3.6.1:
3.6.2:
3.6.3:
	(1).0-->(a)-->1-->(a)-->2-->(b)-->2-->b-->3   accept
	(2).0-->(a)-->0-->(a)-->0-->(b)-->0-->b-->0   no
	(3).0-->(a)-->0-->(a)-->1-->(b)-->1-->b-->1   no
	(4).0-->(a)-->1-->(a)-->2-->(b)-->2-->b-->2   no
	(5).0-->(a)-->1-->(a)-->2-->0-->(b)-->0-->b-->0   no
	(6).0-->(a)-->1-->(a)-->2-->(b)-->2-->0-->b-->0   no
3.6.4:
	(1).0-->(a)-->1-->0-->(a)-->1-->(b)-->2-->(b)-->3   accept
	(2).0-->(a)-->1-->0-->(a)-->1-->(b)-->2-->1-->(b)-->2   no
	(3).0-->(a)-->1-->0-->(a)-->1-->0-->3-->2-->(b)-->3-->2-->(b)-->3   accept
	(3).0-->(a)-->1-->0-->(a)-->1-->0-->3-->2-->1-->(b)-->2-->(b)-->3   accept
	(4).0-->(a)-->1-->0-->(a)-->1-->0-->3-->2-->1-->(b)-->2-->1-->(b)-->2   no
	(5).0-->(a)-->1-->0-->(a)-->1-->0-->3-->2-->(b)-->3-->2-->1-->(b)-->2   no
	(6).0-->3-->(a)-->0-->3-->(a)-->0-->3-->2-->(b)-->3-->2-->1-->(b)-->2   no
	(7).0-->3-->(a)-->0-->3-->(a)-->0-->3-->2-->1-->(b)-->2-->(b)-->3   accept
	...
3.6.5:
正则表达式 --> NFA --> DFA 
3.7.1:
3.7.2:
3.7.3:
3.8.1:0-->(i)-->1-->(f)-->((2))	0-->(epsilon)-->3-->(letter)-->((3))
3.8.2:0-->(epsilon)-->1-->(letter)-->2-->(letter|number)-->((2))
3.8.3:
3.8.4:
模式匹配器的优化:
	1.正则表达式直接生成DFA:Dstates, Dtransition
	2.DFA状态数量最小化:分组合并
	3.生成更加紧凑的转换表:矩阵、链表、四个数组base、default、next、check
3.9.1:
3.9.2:
3.9.3:
3.9.4:
3.9.5:
DFA 和 NFA区别:
	1.任何一个状态对与任何一个输入符号,有且只有一个转换; 2.不存在epsilon的转换
	2.DFA类似于状态转换图

词法分析器的实现理论,特别是词法分析器自动生成工具Lex

第4章

语法分析器
上下文无关文法:简称文法,由终结符号、非终结符号、开始符号、产生式组成
错误处理和错误恢复:
上下文无关语言:可由文法生成的语言
二义性:文法中的句子有多棵语法分析树
有穷自动机不能计数:{((a^n)(b^n))|n>=1}不能使用正则表达式表示
4.2.1:(1).S-->SS*-->SS+S*-->aS+S*-->aa+S*-->aa+a* 
	(2).S-->SS*-->Sa*-->SS+a*-->Sa+a*-->aa+a* 
	(3).
	(4).没有二义性
	(5).后缀加法,后缀乘法,a为基本符号元
4.2.2:(1)S-->0S1-->00S11-->000111	无二义性
4.2.3:
4.2.4:
4.2.5:
4.2.6:
4.2.7:
4.2.8:
消除二义性
消除左递归
提取左公因子
非上下文无关文法,均在语义分析阶段检查。
4.3.1:
4.3.2:
4.3.3:
LL(k)文法:L:从左向右扫描输入、L:表示产生最左推导、k:每一步需要向前看k个字符
错误恢复:恐慌模式、短语层次的恢复
4.4.1:
4.4.2:
4.4.3:
4.4.4:
4.4.5:
4.4.6:
4.4.7:
4.4.8:
4.4.9:
4.4.10:
4.4.11:
4.4.12:
移入-归约语法分析
LR(k)文法 
移入-归约语法分析冲突:移入/归约,归约/归约 
4.5.1:
4.5.2:
4.5.3:
LR(0)自动机
SLR
语法分析表:action goto 
可行前缀
4.6.1:
4.6.2:
4.6.3:
4.6.4:
4.6.5:
4.6.6:
4.6.7:
4.6.8:
4.6.9:
LR(1)
LALR:基于LR(0),更加强大
4.7.1:
4.7.2:
4.7.3:
4.7.4:
4.7.5:
二义性文法:优先级、结合性
错误恢复方法:恐慌模式、短语层次的恢复
4.8.1:
4.8.2:
Yacc(yet another compiler-compiler)
Lex与Yacc:(结合生成编译器前端)
	lex first.l 
	yacc second.y 
	cc y.tab.c -ly -ll
4.9.1:
4.9.2:
4.9.3:
4.9.4:

语法分析器的实现理论,特别是词法分析器自动生成工具Yacc,文法分析如何实现,如何构建语法分析表等。
action和goto函数

第5章 语法制导的翻译

L属性翻译方案(通用型)
S属性翻译方案(综合型)
SSD:注释语法树
5.1.1:
5.1.2:
5.1.3:
依赖图:语法分析树-->注释语法分析树(定义了一个节点求值顺序)
SDD
	S属性:综合属性
	L属性:综合属性+继承属性(必须是左边的或者上方的,不能使用右边的节点)
5.2.1:
5.2.2:
5.2.3:(1)L		(2)L 	(3)S L 	(4)L 
5.2.4:
5.2.5:
5.2.6:
Syntax Directed:类型检查、中间代码生成
抽象语法分析树:继承属性、综合属性、epsilon产生式
5.3.1:
5.3.2:
5.3.3:
SDT(syntax directed translation):语义动作(产生式中的程序片段)
LR-->S属性的SDD		后缀翻译
LL-->L属性的SDD
SDD:语义规则;SDT:语义动作
5.4.1:
5.4.2:
5.4.3:
5.4.4:
5.4.5:
5.4.6:
5.4.7:
LL --> LR:自底向上的方式实现LL文法的L属性SDD(LR文法)
5.5.1:
5.5.2:
5.5.3:
5.5.4:
5.5.5:
5.5.6:

第6章 中间代码生成

DAG(directed acyclic graph):值编码(散列表)
6.1.1:
6.1.2:
三地址代码:四元式、三元式、间接三元式
静态单赋值
6.2.1:
6.2.2:
6.2.3:
类型是有结构的-->类型表达式:
	类比:类、构造函数;类有结构,构造函数就相当于类型构造算子
类型:类型检查、存储空间分配
6.3.1:
6.3.2:
数组元素寻址:addr = base + n*width
6.4.1:
6.4.2:
6.4.3:
6.4.4:
6.4.5:
6.4.6:
6.4.7:
6.4.8:
6.4.9:
类型综合、类型推导
类型转换:拓宽转换、窄化转换;自动类型转换、强制类型转换
置换、合一
6.5.1:
6.5.2:
布尔表达式:控制流跳转、布尔表达式求值
短路代码--> 布尔表达式的短路求值(跳转指令)
if False 优化
6.6.1:
6.6.2:
6.6.3:
6.6.4:
6.6.5:
6.6.6:
6.6.7:
6.6.8:
回填:一趟遍历即可完成
break、continue、goto
6.7.1:
6.7.2:
6.7.3:
switch、case
6.8.1:
函数调用中间代码
过程调用参数、返回值、运行时

第7章 运行时刻环境

存储结构:代码区、静态区、堆、栈
运行时刻栈
活动树
调用者、被调用者
7.2.1:
7.2.2:
7.2.3:
7.2.4:
7.2.5:2*a = 2*(3+1+2) = 12
7.2.6:3*c = 3*(4+1+2+3) = 30
访问链
显示表(哈希表原理)
7.3.1:
7.3.2:
堆区内存管理
存储管理器:分配、回收
存储结构层次:程序局部性(时间局部性、空间局部性)
碎片整理:best-fit next-fit first-fit
内存泄漏、悬空指针
7.4.1:(1)0, 14, 60, 50, 0, 20, 40
	   (2)80, 30, 60, 0, 0, 0, 8 
垃圾回收
	类型安全:Java等,(C/C++语言属于类型不安全的,内存地址可以任意的被访问)
	引用计数:循环数据结构(交叉引用)、开销大
	跟踪对象的可达性
7.5.1:
7.5.2:
基于跟踪的垃圾回收
	标记-清扫式垃圾回收
	Baker标记-清扫式垃圾回收 
	标记-压缩垃圾回收
	拷贝垃圾回收
7.6.1:
7.6.2:
7.6.3:
7.6.4:
时间划分:增量式垃圾回收
空间划分:部分垃圾回收
世代垃圾回收法
列车垃圾回收算法:单节车厢、恐慌模式
7.7.1:
7.7.2:
7.7.3:
7.7.4:
7.7.5:
并行:存在多个线程
并发:同时执行多个程序
C/C++语言垃圾回收:数据表记录分配的内存
弱化引用
7.8.1:

第8章 代码生成

指令选择:RISC CISC
寄存器分配和指派问题(NP):哪些指令分配寄存器,某个指令使用哪个寄存器
指令排序问题(NP):最优的指令排序

汇编语言:
	LD、ST、ADD SUB MUL、BR BLTZ
8.2.1:
	1. LD R1, 1; ST x, R1
	2. LD R1, a; ST x, R1
	3. LD R1, a; ADD R1, R1, 1; ST x, R1 
	4. LD R1, a; LD R2, b; ADD R1, R1, R2; ST x, R1 
	5. LD R1, b; LD R2, c; MUL R1, R1, R2; LD R3, a; ADD R1, R1, R3; ST y, R1
8.2.2: 
	1. LD R1, i; MUL R1, R1, 4; LD R2, a(R1); ST x, R2 
		LD R1, i; MUL R1, R1, 4; LD R2, x; ST a(R1), R2 
8.2.3:
8.2.4:
8.2.5:
8.2.6:2+2+1+2、2+1+2+2、2+2+1+2、
静态分配,固定的绝对地址
栈分配,SP寄存器控制的
8.3.1:
8.3.2:
8.3.3:
基本块、流图
8.4.1:		
8.4.2:		
基本块的优化:
	1.消除公共子结点
	2.消除死代码
	3.常量求值替换
	4.代数恒等式的优化
8.5.1:
8.5.2:
8.5.3:
8.5.4:
8.5.5:
8.5.6:
8.5.7:
8.5.8:
寄存器描述符、地址描述符  -->两张表记录寄存器和变量的地址信息
getReg(I):完成寄存器的选择和分配
8.6.1:
8.6.2:
8.6.3:
8.6.4:
8.6.5:
窥孔优化:
	1.消除冗余代码
	2.消除不可达代码
	3.控制流优化:尽量少采用无条件跳转
	4.代数化简、强度消减
	5.使用特殊机器指令
8.7.1:
8.7.2:
8.7.3:
寄存器分配和指派:图着色算法
8.8.1:
8.8.2:
树重写方案:
	LR语法分析方法
	定义树转换规则
	树匹配方法:
		前缀表示法
		自顶向下的方法
		动态规划算法:代价值
8.9.1:
8.9.2:
8.9.3:
8.9.4:
Ershov number:决定表达式求值需要的寄存器数目
寄存器数目和表达式求值算法关系
8.10.1:
8.10.2:
8.10.3:
8.10.4:
8.10.5:
8.10.6:
8.10.7:
连续求值(子树一棵棵计算完,非跳跃的)
动态规划算法 
代价数组C[i]
8.11.1:
8.11.2:

第9章 机器无关的优化

冗余消除:
	公共子表达式消除
	死代码消除
复制传播
常量折叠(常量代替表达式)
代码移动(循环内移到循环外)
强度消减(乘除-->加减)、归纳变量
9.1.1:
9.1.2:
9.1.3:
9.1.4:
数据流:
	数据流问题:对一组约束求解 --- 线性规划、最优解
	约束:传递函数约束、控制流约束
到达定值
活跃变量
可用表达式
	交汇运算:并集、交集
9.2.1:
9.2.2:
9.2.3:
9.2.4:
9.2.5:
9.2.6:
9.2.7:
9.2.8:
9.2.9:
9.2.10:
9.2.11:
半格
数据流框架迭代算法:前向、逆向
最大不动点 MFP  IDEAL MOP
9.3.1:
9.3.2:
9.3.3:
9.3.4:
9.3.5:
常量传播框架的数据流值:UNDEF NAC C
常量传播框架:单调性、不可分配性
9.4.1:
9.4.2:
冗余:公共子表达式、循环不变表达式、部分冗余表达式
关键边:插入新的基本块
懒惰代码移动:完全冗余、部分冗余
	预期执行表达式:所有的路径都会计算该表达式
	可用表达式:所有路径中都在该点之前被预期执行
	可后延表达式:如果一个程序点,所有路径都有使用该表达式,则可以后延
	被使用的表达式:存在某一条路径使用了该表达式
9.5.1:
9.5.2:
9.5.3:
支配节点(关键节点)
深度优先排序:根-->右-->左 
前进边、后退边、交叉边
回边、可归约性
自然循环:唯一入口点
9.6.1:
9.6.2:
9.6.3:
9.6.4:
9.6.5:
9.6.6:
9.6.7:
9.6.8:
9.6.9:
9.6.10:
9.6.11:
9.6.12:
9.6.13:
基于区域的分析技术:
	循环体区域
	循环区域
不可归约流图:结点分割技术
9.7.1:
9.7.2:
9.7.3:
9.7.4:
9.7.5:
9.7.6:
符号分析:优化、并行化、程序理解的分析
仿射表达式:
	归纳变量:强度消减
	基本归纳变量
	符号化常量
	循环不变变量
9.8.1:
9.8.2:
9.8.3:

第10章 指令级并行性

指令流水线:分支跳转预测错误,就会出现分支延时  
very long instruction word机器 软件静态调度
superscalar 机器 硬件动态调度

代码调度约束:
	控制依赖:条件跳转
	数据依赖:使用前面指令的结果
		真依赖:写 - 读 
		反依赖:读 - 写
		输出依赖:写 - 写
	资源约束:硬件资源是有限的
寄存器使用和并行性之间折衷:硬件寄存器重命名
	先寄存器分配,再指令调度:寄存器少用,并行化少
	先指令调度,再寄存器分配:并行化多,寄存器多用
投机执行、跳转预测
	指令预取
	poison bit
	带断言的执行
10.2.1:
	(1).真依赖 (2).反依赖 (3).输出依赖 (4).真依赖 (5).反依赖 
10.2.2:
10.2.3:
10.2.4:
10.2.5:
	if (a > t){				LDR R1, a;		LDR R2, t;		LDR R3, b;	LDR R4, c;	LDR R5, d;	
		b = a * a;			SUB R2, R2, R1;	MUL R6, R1, R1;	
	}						CMOVZ R3, R6, R2;
	d = a + c;				ADD R5, R1, R4;
数据依赖图
列表调度方法
带优先级的拓扑排序:
	关键路径:数据依赖图中最长的路径
	关键资源:资源使用/可用资源比值最大的资源
10.3.1:	
10.3.2:	
10.3.3:	
10.3.4:	
全局代码调度:同时考虑多个基本块的调度策略
可以投机执行加载运算,不能投机执行保存运算,可以延迟保存运算
支配、反向支配、控制等价
	基于区域的调度算法
	循环展开
动态调度器、静态调度器
10.4.1:
10.4.2:第一步先加载b,c到寄存器中,然后再考虑条件判断
软件流水线化算法:启发式搜索算法
	循环展开
	序言、稳定状态、尾声
Do-Across循环
模数资源预约表:稳定状态的资源预约表
时间间隔T
强连通分量(Strongly Connected Component)
模数变量扩展:可私有化变量 --> 数组 --> 多寄存器存放 --> 消除依赖关系
硬件支持:带断言的指令
10.5.1:
10.5.2:
10.5.3:
10.5.4:
10.5.5:
10.5.6:
10.5.7:
10.5.8:

第11章 并行性和局部性优化

并行化:数据应用
处理器间通信开销
线性规划技术求最优解:
	仿射分划:把迭代的多面体分割成多个部分,在不同机器上执行各个部分
	分块:创建一个由迭代组成的层次结构,一块一块的遍历

对称多处理器:共享内存、一致缓存协议
分布式内存的并行机:不一致内存访问机器(共享内存通信)、消息传递机器(消息传递通信)
Amdahl定律:加速比
任务并行性
master - slave (主从服务器)
栅障同步:并行处理器最后阶段
数据局部性:
	时间局部性 
	空间局部性
向量机:一次性对整个向量进行简单算术运算
仿射变换:
	迭代空间
	数据空间
	处理器空间

改变数据结构的布局
分块技术
高速缓存干扰
11.2.1:单独使用循环清零
循环迭代空间:凸多面体
Fourier-Motzkin消除算法
坐标轴变换:控制执行的顺序
11.3.1:
	(1) for (j = 0; j < 5; j++) X[(7j+10), (7j+11)] = 0;
	(2) for (j = 0; j < 6; j++) X[2j-3] = X[2j-2];
	(3) for (j = 0; j <= 40; j++) X[j+10] = 0;
11.3.2:
11.3.3:
11.3.4:
11.3.5:
11.3.6:
11.3.7:
11.3.8:
11.3.9:
11.3.10:
11.3.11:
数组下标使用仿射表达式:仿射访问
11.4.1:
静态访问:访问指令访问数组
动态访问:循环迭代访问数组
数据复用:
	自复用:
	组复用:
	时间复用:相同位置
	空间复用:相同高速缓存
矩阵:秩、线性无关
矩阵零空间:解 --> 循环迭代指向同一个位置
11.5.1:
11.5.2:
11.5.3:
11.5.4:
11.5.5:
11.5.6:
11.5.7:
数据依赖关系问题:整数线性规划:NP问题
丢番图方程:只考虑整数解的方程
	Greatest Common Divisor 测试
	存在整数解的条件:c % gcd(ai) = 0 
整数线性规划的启发式规则:
	独立变量测试
	循环残数测试
	记忆模式
	分支-界定方法
11.6.1:
11.6.2:
11.6.3:
11.6.4:
11.6.5:
11.6.6:
11.6.7:
11.6.8:
11.6.9:
并行性:将计算任务分解成尽可能多的独立单元
仿射空间分划
空间分划约束:具有数据依赖的运算必须分配到同一个处理器上
高斯消除法:根据等式减少变量数目
卫式:增加的测试条件
消除空迭代:收紧循环的界限
消除条件测试:分割循环
基本仿射转换:
	融合
	裂变
	重新索引
	比例变换
	幺模转换:幺模系数矩阵,没有常量向量
		反置
		交换
		倾斜
11.7.1:
11.7.2:
11.7.3:
11.7.4:
11.7.5:
11.7.6:
11.7.7:
11.7.8:
同步运算:同步栅障
程序依赖图(PDG):发现循环裂变的机会
	环连接的语句不能被分割
	单向的可以分割
11.8.1:
11.8.2:
11.8.3:
流水线化技术:将任务分成数个阶段,各个阶段在不同的处理器上运行
	降低通信量(数据仅在相邻的处理器上共享)
完全可交换循环:时间分划约束具有多个独立解
	多个循环可以任意地排列而不会改变源程序的语义
SOR:连续过松弛法(Successive Over Relaxing)
波阵面:波阵面推进
K个最外层完全可交换循环  --> K-1度的流水线化并行度
Farkas引理:时间分划约束求解
11.9.1:
11.9.2:
11.9.3:
11.9.4:
11.9.5:
局部性优化:
	时间局部性
	数组收缩
	空间局部性(写数据、只读数据)
条状挖掘(stripmining)
11.10.1:
	for(i = 0; i < n; i++){
		T = A[i] + B[i];
		D[i] = T + C[i];
	}
11.10.2:
	for(i = 0; i < n; i++){
		T = A[i] + B[i];
		S = C[i] + D[i];
		E[i] = T + S;
	}
11.10.3:
	for (j = 0; j < n; j += d){
		for (i = n-1; i >= 0; i--){
			for (k = j; k < min(n, j+d); k++){
				S;
			}
		}
	}
仿射转换的应用:
	分布式内存计算机:发送消息通信  --> 给处理器分配大型、独立的计算单元
		数据分配
		通信代码
		优化:权衡同步与通信
	多指令发送处理器:流水线化
		充分利用所有的功能单元
	向量指令、SIMD指令:一条指令可以对很多数据进行处理
	数据预取:将处理器马上要使用的数据提前加载到缓存中。
		不能太晚、不能过早

第12章 过程间分析

过程间分析 <--> 过程内分析
	指针别名分析:二分决策图

调用图:
	调用点
	虚方法(virtual method/function)
上下文无关分析
调用串
基于克隆的上下文相关分析
基于摘要的上下文相关分析
12.1.1:
12.1.2:
过程间分析的作用:
	虚方法调用:内联频繁调用的方法
	指针别名分析:
	并行化: 
	软件错误和漏洞检测:
		SQL注入:输入非法,控制数据库
		缓冲区溢出:输入非法,控制操作系统
		
Datalog元素:
	atom:基础原子(参数都是常量的断言)
	断言
	变量或常量项
断言:
	in(B, D)
	equals(X, a)
	edge(X, Y)
	path(X, Y)
规则的子目标:Bi(H)
EDB(外延数据库断言)
IDB(内涵数据库断言)
	def(B, N, X):基本块B的第N个语句可以对X定值
	succ(B, N, C):基本块C是基本块B的后继,基本块B有N个语句
	rd(B, N, C, M, X):基本块C的第M个语句对X定值,到达了B的第N个语句
	assign(B, N, X)
	type(X, T):X的类型为T
增量式形式
有问题的Datalog规则:
	不安全规则
	不可分层的程序
12.3.1:
12.3.2:
12.3.3:
12.3.4:
12.3.5:
12.3.6:
12.3.7:
指针指向分析
	基于包含的分析技术
	基于等价关系的分析
控制流无关分析:不考虑程序的执行顺序,没有kill的概念
影响指针的语句:
	对象创建:
	复制语句:
	字段保存:
	字段读取:
pts(V, H):new、=
hpts(H, F, G):字段保存、字段读取
类型信息:
	vType(V, T):变量V的类型为T
	hType(H, T):堆对象H的类型为T
	assignable(T, S):S对象可以赋值给T对象
12.4.1:
	a --> h 
	b --> g 
	c --> h 
	a.f --> h.f --> g 
	b.f --> g.f --> h 
	d --> c.f --> h.f --> g 
12.4.2:
12.4.3:
调用图:上下文无关和控制流无关的指针指向分析生成(类型声明和语法分析生成调用图)
	actual(S, I, V):V是调用点S上的第I个实在参数
	formal(S, I, V):V是方法S中声明的第I个形式参数
	cha(T, N, M):一个类型为T的接收对象,调用N时,实际调用的方法是M (class hierarchy analysis 类层次结构分析)
	
	invokes(S, M):调用点S可能调用方法M
反射机制:动态创建对象的类型,方法名等
12.5.1:
12.5.2:
上下文相关性可以提高过程间分析的精确性
上下文环境信息:全局性查询
二分决策图(BDD)
上下文是调用串的表示形式
平凡的SCC、非平凡的SCC
对象相关性
12.6.1:
	p在c1处调用q
	q在c2、c3处递归调用q
	q在c4处调用r
12.6.2:
	q是递归的
	调用r的上下文:
		1.(c1, c4)、(c1, c2, c4)、(c1, c3, c4)...
12.6.3:
BDD(二分决策图):用图表示布尔函数的方法,把布尔函数表示成一个带根的DAG
	一个根节点、两个叶子节点
	低边:节点对应的变量取值为0
	高边:节点对应的变量取值为1
短路:消除无效的节点
节点合并:合并子节点完全相同的节点
主要的关系运算:
	初始化
	合并
	投影
	连接
12.7.1:
12.7.2:
	2n-1
12.7.3:
	AND逻辑:
	N,M两个节点,N,M都取1则为1,否则为0.
12.7.4:

总结

编译原理这本书共649页,12章。1-2章属于内容背景和概述,相当于编译器基础知识的入门和编译器主体知识的一个归纳简介;3-6章属于具体的编译原理的主要内容讲解,主要包含了编译器前端部分的核心内容:词法分析、语法分析、语义分析、中间代码生成;7章属于内存管理,主要讲解了内存模型和内存分配等;第8章讲解了代码生成,属于编译后端的核心,生成目标代码,解释中间代码如何生成目标程序。9-12章属于编译器优化部分,如何优化编译器,提高编译器生成的目标程序的性能,优化技术和方法很多:代码自身的优化、指令级优化、存储结构等体系结构层面的优化、过程间分析优化等。

这本书的内容很多,理解起来很不容易,参考文献更加多,提高了很多算法的来源和发展,很适合大牛进一步研究,毕竟参考蚊香都有简介,说明了哪些原理和技术,在哪些论文或书中第一次出现啥的。

读后感

这本书坚持看完真的不容易,600多页,专业的书籍,一般一天我只能看10页左右,如果状态好,坚持看一整天,也不超过50页,所以我看书真的是很慢很慢,很拖拉。

这本书习题说多不是很多,但是也不少。总的来说,习题很复杂,难度很高,开始还能坚持做,目标是把所有的习题做一遍,后来发现很多不会做,也找不到什么参考答案什么的,做题进度也是超级慢,严重影响看书速度,因此很多习题直接放弃了。

能够坚持把书看完一遍,也算是一种收获吧。一本一本的书看完,才能够知道书说的是什么,而没有遗漏太多的东西,和大牛聊天也自信一点。

编译原理,果然书如其名,没有骗人,讲的就是原理性的东西,理论性太强了,和实际编码实现编译器还是差距很大的,可能本人水平太差,没有发现共同点,反正看完,依旧不知道怎么手动一个编译器,入门也不知道。附录中的编译器前端源代码很不错,对于写编译器,或者理解编译器作用更明显,可惜只有一个前端,看完附录,才知道编译器应该怎么实现,怎么入手,收获反而更大。可能个人也是喜欢看得见和摸的着东西,务实更多,对理论有畏惧心,不善于高层次的抽象。

看完编译器原理,最大的收获自然是知道了编译器的实现原理,书中有很多抽象的算法,面试,吹牛逼应该非常好用,知道进行了,能不能实现完全是另外一回事。真心感觉非常抽象,只有部分可以看明白怎么回事,但是距离代码实现还是有很多的路要走。书中的算法,基本是抽象代码实现的,伪代码的水平应该还不到吧,这是我不喜欢的第二点,我喜欢《数据结构与算法分析》那样风格的书,代码示例非常多,伪代码水平非常高,基本和源代码差不多,很容易就copy、实现(太懒,不善于独立思考和写代码的码农)。

编译原理,个人感觉不适合刚入门的新手去研究,理论性太强了,状态机、正则表达式、启发式等看得晕晕的。比较适合有扎实功底的选手,专门去研究编译器实现原理,抽象层次的总结和归纳。不适合想实现一个编译器的选手去研究,这样的入门太困难了,还不如弄一个简单、傻瓜式的解释器、编译器,再慢慢去添加新功能,优化提升算法等,迭代式的开发一个编译器,更加靠谱。反过来说,非常适合写过编译器的大牛看,能够明白写的编译器为什么要那么写,或者说编译器应该怎么优化,提高性能。这本书对于代码优化和性能优化讲了很多,给了很多方法和算法。

由于很多内容看的不是非常明白,所以我也不知道这本书内容写的好不好,书中提到的算法和算法的应用场景非常多和有效,可惜精华的部分,我都没有吸收,尴尬一逼。

立目标
1.回头看第二遍吧,仔细研究研究编译器!时间就不知道了,唉。
2.使用C++语言,把编译器前端代码写一遍,巩固巩固学习的内容,再理解理解编译原理。

补充:

学习编译原理,首先需要学习离散数学,具有一定的数学功底,否则看文法、正则表达式和自动机很迷茫。原本以为离散数学只是一些数学基础,集合、概率等,学过机器学习,知道一些逻辑、谓词逻辑等知识,最近看到Stanford的计算机基础那本书,才知道离散数学非常非常非常重要,是计算机科学的基础,里面就有上下文文法、正则表达式、(非)确定自动机等知识。因此,才明白学习离散数学是学习编译原理的基础。

Start Date:2016-12-22

End Date: 2017-03-30