1.编译器
1.1 三阶段编译器
- 编译器前端: 次要用于了解源代码、扫描解析源代码并进行语义表白
- IR: Intermediate Representation,可能有多个,编译器会应用多个 IR 阶段、多种数据结构示意程序,并在两头阶段对代码进行屡次优化
- 优化器: 次要目标是升高程序资源的耗费,但有实践曾经表明某些优化存在着NP难题,所以编译器无奈进行最佳优化,通常罕用折中计划
- 编译后端: 次要用于生成特定指标机器上的程序,可能是可执行文件,也可能是须要进一步解决的obj、汇编语言等
1.2 Go语言编译器
src/cmd/compile/internalTips: 留神:大写的GC示意垃圾回收
2.词法解析
go/src/cmd/compile/internal/syntax/tokens.gogo/src/go/scanner、go/src/go/token
2.1 scanner.go 代码简介
go/src/go/scanner
const (
ScanComments Mode = 1 << iota // 返回正文作为正文标记
dontInsertSemis // 不要主动插入分号-仅用于测试
)
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
2.2 token.go 代码代码简介
go/src/go/token
- init()函数: 初始化关键字keywords变量(map[string]Token)的值
map[break:61 case:62 chan:63 const:64 continue:65 default:66 defer:67 else:68 fallthrough:69 for:70 func:71 go:72 goto:73 if:74 import:75 interface:76 map:77 package:78 range:79 return:80 select:81 struct:82 switch:83 type:84 var:85]
- String()函数: 返回与令牌 tok 对应的字符串,对于操作符、分隔符和关键字,字符串是理论的标记字符序列(例如,对于标记ADD,字符串是“+”),对于所有其余令牌,字符串对应于令牌常量名(例如,对于令牌IDENT,字符串为“IDENT”)
- Precedence()函数: 返回二进制操作符 op 的操作符优先级,如果op不是二进制操作符,则后果为最低优先级
- Lookup()函数: 查找标记符对应的token
- IsLiteral() 函数: 判断token值是否是根本类型范畴的值,若是返回true,否则返回false
literal_beg
// Identifiers and basic type literals
// (these tokens stand for classes of literals)
IDENT // main
INT // 12345
FLOAT // 123.45
IMAG // 123.45i
CHAR // 'a'
STRING // "abc"
literal_end
- IsOperator() 函数: 判断token值是否是操作范畴的值,若是返回true,否则返回false
operator_beg
// Operators and delimiters
ADD // +
SUB // -
MUL // *
QUO // /
REM // %
AND // &
OR // |
XOR // ^
SHL // <<
SHR // >>
AND_NOT // &^
ADD_ASSIGN // +=
SUB_ASSIGN // -=
MUL_ASSIGN // *=
QUO_ASSIGN // /=
REM_ASSIGN // %=
AND_ASSIGN // &=
OR_ASSIGN // |=
XOR_ASSIGN // ^=
SHL_ASSIGN // <<=
SHR_ASSIGN // >>=
AND_NOT_ASSIGN // &^=
LAND // &&
LOR // ||
ARROW // <-
INC // ++
DEC // --
EQL // ==
LSS // <
GTR // >
ASSIGN // =
NOT // !
NEQ // !=
LEQ // <=
GEQ // >=
DEFINE // :=
ELLIPSIS // ...
LPAREN // (
LBRACK // [
LBRACE // {
COMMA // ,
PERIOD // .
RPAREN // )
RBRACK // ]
RBRACE // }
SEMICOLON // ;
COLON // :
operator_end
- IsKeyword() 函数: 判断token值是否是关键字范畴的值,若是返回true,否则返回false
keyword_beg
// Keywords
BREAK
CASE
CHAN
CONST
CONTINUE
DEFAULT
DEFER
ELSE
FALLTHROUGH
FOR
FUNC
GO
GOTO
IF
IMPORT
INTERFACE
MAP
PACKAGE
RANGE
RETURN
SELECT
STRUCT
SWITCH
TYPE
VAR
keyword_end
3.语法解析
go/src/cmd/compile/internal/syntax/nodes.gogo/src/cmd/compile/internal/syntax/parser.go
//包导入申明
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .
//动态常量
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
//类型申明
TypeSpec = identifier [ "=" ] Type .
//变量申明
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
3.1 nodes.go 代码代码简介
AssignStmt struct {
Op Operator // 示意以后的操作符,即":=",0 示意没有操作
Lhs, Rhs Expr // Lhs, Rhs 别离代表左右两个表达式,Rhs == ImplicitOne means Lhs++ (Op == Add) or Lhs-- (Op == Sub)
simpleStmt
}
//Pos() 返回与节点相关联的地位,如下所示:
// 1)示意终端语法产物(Name,BasicLit等)的节点地位是相应产物在源中的地位。
// 2)示意非终端产品(IndexExpr, IfStmt等)的节点的地位是与该产品惟一关联的令牌的地位;通常是最右边的一个('['示意IndexExpr, 'if'示意IfStmt,等等)
type Node interface {
Pos() Pos
aNode()
}
3.2 parser.go 代码代码简介
type parser struct {
file *PosBase
errh ErrorHandler
mode Mode
scanner
base *PosBase // 以后地位的基准
first error // 遇到的第一个谬误
errcnt int // 遇到的谬误数
pragma Pragma // 编译批示标记
fnest int // 函数嵌套级别(用于错误处理)
xnest int // 表达式嵌套级别(用于解决编译歧义)
indent []byte //跟踪反对
}
const stopset uint64 = 1<<_Break |
1<<_Const |
1<<_Continue |
1<<_Defer |
1<<_Fallthrough |
1<<_For |
1<<_Go |
1<<_Goto |
1<<_If |
1<<_Return |
1<<_Select |
1<<_Switch |
1<<_Type |
1<<_Var
3.3 语法解析举例
a := b + c(10)
//Tips: c(10) 示意强类型转换。
a := b + c(10)语句被语法解析后转换为对应的 syntax.AssignStmt 构造体之后,最顶层的 Op 操作符为 token.Def(:=)。Lhs 表达式类型为标识符syntax.Name,值为标识符 “a”。Rhs表达式为 syntax.Operator 加法运算,加法运算右边为标识符 “b”,左边为函数调用表达式,类型为 CallExpr,其中,函数名 c 的类型为 syntax.Name,参数为常量类型 syntax.BasicLit,代表数字 10。
4.形象语法树构建
func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
var cs constState
for _, decl := range decls {
p.setlineno(decl)
switch decl := decl.(type) {
case *syntax.ImportDecl:
p.importDecl(decl)
case *syntax.VarDecl:
l = append(l, p.varDecl(decl)...)
case *syntax.ConstDecl:
l = append(l, p.constDecl(decl, &cs)...)
case *syntax.TypeDecl:
l = append(l, p.typeDecl(decl))
case *syntax.FuncDecl:
l = append(l, p.funcDecl(decl))
default:
panic("unhandled Decl")
}
}
return
}
5.类型查看
go/src/cmd/compile/internal/gc/typecheck.go
6.变量捕捉
package main
import (
"fmt"
)
func main() {
a := "qinshixian"
b := make(map[string]int)
c := "haoweilai"
go func() {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}()
a = "asddss"
}
7.函数内联
package main
import "testing"
//go:noinline
func max(a, b int) int {
if a > b {
return a
}
return b
}
var Result int
func BenchmarkMax(b *testing.B) {
var r int
for i := 0; i < b.N; i++ {
r = max(-1, i)
}
Result = r
}
//Tips://go:noinline 示意以后函数禁止函数内联优化。
没有加上正文 //go:noinline 运行 go test leetcode_test.go -bench=. 如下图:
函数外部有 for、range、go、select 等语句时,如下图:
若想查看函数是否能够应用函数内联,可应用 命令 go tool compile -m=2 leetcode_test.go,如下图所示:
8.逃逸剖析
(1)准则1:指向栈上对象的指针不能被存储到堆中
(2)准则2:指向栈上对象的指针不能超过该栈对象的生命周期
package main
var n *int
func escape(){
a := 100
n = &a
}
func main() {
escape()
}