-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathgolang_compiler_source_analysis
512 lines (437 loc) · 22.5 KB
/
golang_compiler_source_analysis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
GOLANG编译器源码流程分析及控制指令集的方法
一、工具准备
GOLANG编译器的代码主要位于cmd/compile目录下,但代码量较多,调用关系复杂,为了便于快速分析,利用go-package-plantuml工具先把代码转化为UML图,然后根据UML图从整体上自上而下地分析。
参考了文档:https://studygolang.com/articles/9719 来生成UML图,但在实践的过程中,需要进行一些修改才跑得通,记录如下:
1、搭建好go的环境
下载go源码、编译go1.4、编译go1.10最新版本,此处不再赘述。
2、安装go-package-plantuml工具
首先下载代码(https://gitee.com/jscode/go-package-plantuml),然后把go-package-plantuml拷贝到实验室机器的$GOROOT/src目录下。因为我们实验室的机器无法直接访问外网,所以还需要修改一下代码:
diff --git a/main.go b/main.go
index 97143de..5ab63e1 100644
--- a/main.go
+++ b/main.go
@@ -1,7 +1,7 @@
package main
import (
- "git.oschina.net/jscode/go-package-plantuml/codeanalysis"
+ "go-package-plantuml/codeanalysis"
log "github.com/Sirupsen/logrus"
"fmt"
"github.com/jessevdk/go-flags"
最后安装go-package-plantuml即可:
# go install go-package-plantuml
3、安装plantuml工具
由于sourceforge在公司里面无法访问,所以找了一条曲线下载的方法,在github的一个开源项目上,下载plantuml的压缩包:https://github.com/jvantuyl/sublime_diagram_plugin/tree/master/diagram
下载plantuml.1.2018.1.jar。
此外,由于centos自带的Graphviz版本太老,绘制uml图会有问题,需要下载最新的源码(https://graphviz.gitlab.io/_pages/Download/Download_source.html),然后再编译、安装。
4,工具使用
1)生成go代码的UML文本文件
使用go-package-plantuml,分析/home/go/go_main/src/cmd/compile目录的代码,生成uml的文本文件。命令如下:
go-package-plantuml --codedir /home/go/go_main/src/cmd/compile --gopath /home/go/go_main --outputfile /tmp/result
go-package-plantuml有bug,指定的输出outfile不生效,始终生成到/tmp/uml.txt文件中。
2)根据UML文本文件生成图片
使用plantuml解析上一步生成的uml.txt,生成svg格式的图片。命令如下:
java -jar plantuml.1.2018.1.jar ./all_uml.txt -tsvg
3)最终得到的UML图的svg文件如下
二、GOLANG编译过程简介
和传统的编译器一样,功能也分为4个阶段:
1、词法和语法分析
代码在cmd/compile/internal/syntax目录下,经过这一阶段的处理后,生成语法树。
Syntax对外提供的接口为Parse,其含义如下:
// Parse parses a single Go source file from src and returns the corresponding
// syntax tree. If there are errors, Parse will return the first error found,
// and a possibly partially constructed syntax tree, or nil if no correct package
// clause was found. The base argument is only used for position information.
//
// If errh != nil, it is called with each error encountered, and Parse will
// process as much source as possible. If errh is nil, Parse will terminate
// immediately upon encountering an error.
//
// If a PragmaHandler is provided, it is called with each pragma encountered.
//
// If a FilenameHandler is provided, it is called to process each filename
// encountered in //line directives.
//
// The Mode argument is currently ignored.
func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, fileh FilenameHandler, mode Mode) (_ *File, first error)
传入单个的go文件,经词法分析、语法分析之后,返回语法树,语法树是存贮到File中的。File结构体又是node结构体的子类。涉及的部分结构体的关系截图:
Parse函数的内部实现,涉及到source、scanner等父类,以及声明(Decl,包括变量声明VarDecl、类型声明TypeDecl等)、语句(Stmt,包括返回值语句ReturnStmt、代码块BlockStmt、赋值语句AssignStmt、分支BranchStmt等等)。后继有时间再具体分析。
在编译器的主函数中,词法、语法分析对应的入口代码段如下:
src/cmd/compile/internal/gc/main.go, Main主函数:
timings.Start("fe", "parse")
lines := parseFiles(flag.Args())
timings.Stop()
2、类型检查
对上一步构造出的语法树进行若干次类型检查。代码主要是对语法树中的节点(即Node数据结构),调用typecheck函数:
// typecheck type checks node n.
// The result of typecheck MUST be assigned back to n, e.g.
// n.Left = typecheck(n.Left, top)
func typecheck(n *Node, top int) *Node
比如下面的调用:
// Phase 1: const, type, and names and types of funcs.
......
timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
xtop[i] = typecheck(n, Etop)
}
}
// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.
// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
xtop[i] = typecheck(n, Etop)
}
}
入口调用的代码段大同小异,都是根据各种条件的组合情况,反复调用typecheck函数。而typecheck函数的实现细节很多,这里只是进行流程梳理,暂不进行细节分析。
3、中间代码生成
GOLANG使用SSA(Static Single Assignment)作为中间语言。
src/cmd/compile/internal/gc/main.go, Main主函数中的入口如下:
// Prepare for SSA compilation.
// This must be before peekitabs, because peekitabs
// can trigger function compilation.
initssaconfig()
// Just before compilation, compile itabs found on
// the right side of OCONVIFACE so that methods
// can be de-virtualized during compilation.
Curfn = nil
peekitabs()
// Phase 8: Compile top level functions.
// Don't use range--walk can add functions to xtop.
timings.Start("be", "compilefuncs")
fcount = 0
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if n.Op == ODCLFUNC {
funccompile(n)
fcount++
}
}
timings.AddEvent(fcount, "funcs")
函数调用关系如下:
funccompile -> compileSSA -> buildssa
而buildssa把语法树节点的Node转化为ssa.Func,其描述如下:
// buildssa builds an SSA function for fn.
func buildssa(fn *Node, worker int) *ssa.Func
ssa.Func是一个比较核心的数据结构,表示一个函数,它和很多数据结构都有关联。
// A Func represents a Go func declaration (or function literal) and its body.
// This package compiles each Func independently.
// Funcs are single-use; a new Func must be created for every compiled function.
4、机器指令生成
和上一步的入口是一样的,funccompile -> compileSSA ,
compileSSA函数里面,先调用buildssa生成中间代码,然后再调用genssa把中间代码放入Progs结构中,最后调用Flush生成机器指令:
// compileSSA builds an SSA backend function,
// uses it to generate a plist,
// and flushes that plist to machine code.
// worker indicates which of the backend workers is doing the processing.
func compileSSA(fn *Node, worker int) {
f := buildssa(fn, worker)
if f.Frontend().(*ssafn).stksize >= maxStackSize {
largeStackFramesMu.Lock()
largeStackFrames = append(largeStackFrames, fn.Pos)
largeStackFramesMu.Unlock()
return
}
pp := newProgs(fn, worker)
genssa(f, pp)
pp.Flush()
// fieldtrack must be called after pp.Flush. See issue 20014.
fieldtrack(pp.Text.From.Sym, fn.Func.FieldTrack)
pp.Free()
}
Progs.Flush的定义如下:
// Flush converts from pp to machine code.
func (pp *Progs) Flush() {
plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
obj.Flushplist(Ctxt, plist, pp.NewProg, myimportpath)
}
obj已经属于汇编器。Progs表示函数中的指令,也是一个比较核心的数据结构,可以根据它生成机器指令。
// Progs accumulates Progs for a function and converts them into machine code.
三、ARM控制指令集的方法
上面对GOLANG的代码流程进行了大体的分析,现在结合ARM结构,分析GOLANG是如何控制指令集的。
ARM架构的指令集有ARMv5、ARMv6、ARMv7等,同样的go代码,在不同的架构下,需要生成不同的机器指令。前面的语法分析、类型检查、中间代码生成和机器指令生成,只需要在第三步和第四步进行修改,就可以实现这个功能。
1、在全局变量中引入了GOARM变量来标识指令集
可以看到这是一个通用的方法,ARM、x86、MIPS等架构都有各自的变量,来保存各自的指令集:
var (
defaultGOROOT string // set by linker
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)
GO386 = envOr("GO386", defaultGO386)
GOARM = goarm()
GOMIPS = gomips()
Version = version
)
2、GOLANG工具链制作时,生成文件,保存GOARM的值
// mkzbootstrap writes cmd/internal/objabi/zbootstrap.go:
//
// package objabi
//
// const defaultGOROOT = <goroot>
// const defaultGO386 = <go386>
// const defaultGOARM = <goarm>
// const defaultGOMIPS = <gomips>
// const defaultGOOS = runtime.GOOS
// const defaultGOARCH = runtime.GOARCH
// const defaultGO_EXTLINK_ENABLED = <goextlinkenabled>
// const version = <version>
// const stackGuardMultiplier = <multiplier value>
// const goexperiment = <goexperiment>
//
// The use of runtime.GOOS and runtime.GOARCH makes sure that
// a cross-compiled compiler expects to compile for its own target
// system. That is, if on a Mac you do:
//
// GOOS=linux GOARCH=ppc64 go build cmd/compile
//
// the resulting compiler will default to generating linux/ppc64 object files.
// This is more useful than having it default to generating objects for the
// original target (in this example, a Mac).
func mkzbootstrap(file string) {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "package objabi\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOOS = runtime.GOOS\n")
fmt.Fprintf(&buf, "const defaultGOARCH = runtime.GOARCH\n")
fmt.Fprintf(&buf, "const defaultGO_EXTLINK_ENABLED = `%s`\n", goextlinkenabled)
fmt.Fprintf(&buf, "const version = `%s`\n", findgoversion())
fmt.Fprintf(&buf, "const stackGuardMultiplier = %d\n", stackGuardMultiplier())
fmt.Fprintf(&buf, "const goexperiment = `%s`\n", os.Getenv("GOEXPERIMENT"))
writefile(buf.String(), file, writeSkipSame)
}
3、Go启动的时候,还可以通过环境变量的方式,更改GOARM的值
// xinit handles initialization of the various global state, like goroot and goarch.
func xinit() {
b := os.Getenv("GOROOT")
if b == "" {
fatalf("$GOROOT must be set")
}
goroot = filepath.Clean(b)
......
b = os.Getenv("GOARM")
if b == "" {
b = xgetgoarm()
}
goarm = b
......
4、编译go程序的时候,通过GOARM的取值,来标识不同的指令集
envOr函数优先从环境变量中读取,如果为空,再从工具链制作时生成的文本文件中读取。
func goarm() int {
switch v := envOr("GOARM", defaultGOARM); v {
case "5":
return 5
case "6":
return 6
case "7":
return 7
}
// Fail here, rather than validate at multiple call sites.
log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
panic("unreachable")
}
5、编译go程序、生成指令的是偶,根据GOARM的值,选择不同的指令
SSA的规则文件中的部分代码片段如下:
// count trailing zero for ARMv5 and ARMv6
// 32 - CLZ(x&-x - 1)
(Ctz32 <t> x) && objabi.GOARM<=6 -> (RSBconst [32] (CLZ <t> (SUBconst <t> (AND <t> x (RSBconst <t> [0] x)) [1])))
// count trailing zero for ARMv7
(Ctz32 <t> x) && objabi.GOARM==7 -> (CLZ <t> (RBIT <t> x))
6、运行时库,也是根据GOARM来选择指令的
运行时库中的部分代码片段如下:
// armPublicationBarrier is a native store/store barrier for ARMv7+.
// On earlier ARM revisions, armPublicationBarrier is a no-op.
// This will not work on SMP ARMv6 machines, if any are in use.
// To implement publicationBarrier in sys_$GOOS_arm.s using the native
// instructions, use:
//
// TEXT ·publicationBarrier(SB),NOSPLIT,$-4-0
// B runtime·armPublicationBarrier(SB)
//
TEXT runtime·armPublicationBarrier(SB),NOSPLIT,$-4-0
MOVB runtime·goarm(SB), R11
CMP $7, R11
BLT 2(PC)
WORD $0xf57ff05e // DMB ST
RET
可以看到,ARM架构通过引入GOARM变量,可以在编译时选择不同的指令,也可以在运行时库中自动选择不同的指令。GOARM变量的值是再制作GOLANG工具链的时候写入到文件中的,而且还可以通过环境变量,在运行的时候动态修改GOARM的值。
四、控制ppc大端e6500指令集的方法
我们可以参考ARM的实现,通过添加GOPPC变量来控制指令集。具体实现如下:
1、引入了GOPPC环境变量,使用它来标识CPU类型:
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 49ed800..9aaf33c 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -31,6 +31,7 @@ var (
goarm string
go386 string
gomips string
+ goppc string
goroot string
goroot_final string
goextlinkenabled string
@@ -145,6 +146,9 @@ func xinit() {
}
gomips = b
+ b = os.Getenv("GOPPC")
+ goppc = b
+
if p := pathf("%s/src/all.bash", goroot); !isfile(p) {
fatalf("$GOROOT is not set correctly or not exported\n"+
"\tGOROOT=%s\n"+
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index eafef6b..92e4924 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -27,6 +27,7 @@ var (
GO386 = envOr("GO386", defaultGO386)
GOARM = goarm()
GOMIPS = gomips()
+ GOPPC = goppc()
Version = version
)
@@ -53,6 +54,25 @@ func gomips() string {
panic("unreachable")
}
+type CpuType uint
+
+const (
+ PpcDefault CpuType = iota
+ PpcE6500
+)
+
+func goppc() CpuType {
+ switch v := envOr("GOPPC", defaultGOPPC); v {
+ case "e6500":
+ return PpcE6500
+ case "":
+ return PpcDefault
+ }
+ // Fail here, rather than validate at multiple call sites.
+ log.Fatalf("Invalid GOPPC value. Must be e6500 or empty.")
+ panic("unreachable")
+}
+
2、golang工具链制作成功后,把当前的GOPPC环境变量的值持久化到文件中
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 5cbcd81..b8240cd 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -44,6 +44,7 @@ func mkzversion(dir, file string) {
// const defaultGO386 = <go386>
// const defaultGOARM = <goarm>
// const defaultGOMIPS = <gomips>
+// const defaultGOPPC = <goppc>
// const defaultGOOS = runtime.GOOS
// const defaultGOARCH = runtime.GOARCH
// const defaultGO_EXTLINK_ENABLED = <goextlinkenabled>
@@ -71,6 +72,7 @@ func mkzbootstrap(file string) {
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
+ fmt.Fprintf(&buf, "const defaultGOPPC = `%s`\n", goppc)
fmt.Fprintf(&buf, "const defaultGOOS = runtime.GOOS\n")
fmt.Fprintf(&buf, "const defaultGOARCH = runtime.GOARCH\n")
fmt.Fprintf(&buf, "const defaultGO_EXTLINK_ENABLED = `%s`\n", goextlinkenabled)
3、Golang主分支代码已经删掉了对于PPC64大端的支持,需要回退。
需要回退如下补丁:
https://github.com/golang/go/commit/7f5302560424b9ff219dbf217801615fef3b1f38
https://github.com/golang/go/commit/a9e76a2583ce24c1773e81310dc208e51f641983
https://github.com/golang/go/commit/1f57372cb1c2c96254bb16fdb41295d0c37f33d5
https://github.com/golang/go/commit/cc306c2d2c77b988e0321f14ab1951c26d8fa070
https://github.com/golang/go/commit/81e73292faf10c0f7cda5b6614e3909c17cf4f31
https://github.com/golang/go/commit/40e25895e3ab998033cc9f7086332d046c7f608a
https://github.com/golang/go/commit/be943df58860e7dec008ebb8d68428d54e311b94
回退补丁之后,新增了一个OldArch变量来表示是否PPC64大端(目前PPC64大端只有E6500处理器):
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index ae6caee..5576dec 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -37,6 +37,7 @@ type Config struct {
nacl bool // GOOS=nacl
use387 bool // GO386=387
SoftFloat bool //
+ OldArch bool // True for older versions of architecture, e.g. true for PPC64BE, false for PPC64LE
NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool //
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
@@ -216,7 +217,9 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.hasGReg = true
c.noDuffDevice = objabi.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
case "ppc64":
+ c.OldArch = true
c.BigEndian = true
+ c.NeedsFpScratch = true
fallthrough
case "ppc64le":
c.PtrSize = 8
通过OldArch变量来控制是否生成E6500处理器支持的指令。
4、和ARM的实现方案相比,OldArch是冗余的。我们可以把它删掉,利用第一步中的GOPPC环境变量来达到同样的目的。
部分代码修改如下:
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 5576dec..d7202d6 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -37,7 +37,6 @@ type Config struct {
nacl bool // GOOS=nacl
use387 bool // GO386=387
SoftFloat bool //
- OldArch bool // True for older versions of architecture, e.g. true for PPC64BE, false for PPC64LE
NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool //
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
@@ -217,7 +216,6 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.hasGReg = true
c.noDuffDevice = objabi.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
case "ppc64":
- c.OldArch = true
c.BigEndian = true
c.NeedsFpScratch = true
fallthrough
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 91c8dc3..eb09efd 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -57,15 +57,15 @@
(Div64F x y) -> (FDIV x y)
// Lowering float <-> int
-(Cvt32to32F x) && config.OldArch -> (FRSP (FCFID (Xi2f64 (SignExt32to64 x))))
-(Cvt32to64F x) && config.OldArch -> (FCFID (Xi2f64 (SignExt32to64 x)))
-(Cvt64to32F x) && config.OldArch -> (FRSP (FCFID (Xi2f64 x)))
-(Cvt64to64F x) && config.OldArch -> (FCFID (Xi2f64 x))
+(Cvt32to32F x) && objabi.GOPPC == objabi.PpcE6500 -> (FRSP (FCFID (Xi2f64 (SignExt32to64 x))))
+(Cvt32to64F x) && objabi.GOPPC == objabi.PpcE6500 -> (FCFID (Xi2f64 (SignExt32to64 x)))
+(Cvt64to32F x) && objabi.GOPPC == objabi.PpcE6500 -> (FRSP (FCFID (Xi2f64 x)))
+(Cvt64to64F x) && objabi.GOPPC == objabi.PpcE6500 -> (FCFID (Xi2f64 x))
-(Cvt32Fto32 x) && config.OldArch -> (Xf2i64 (FCTIWZ x))
-(Cvt32Fto64 x) && config.OldArch -> (Xf2i64 (FCTIDZ x))
-(Cvt64Fto32 x) && config.OldArch -> (Xf2i64 (FCTIWZ x))
-(Cvt64Fto64 x) && config.OldArch -> (Xf2i64 (FCTIDZ x))
+(Cvt32Fto32 x) && objabi.GOPPC == objabi.PpcE6500 -> (Xf2i64 (FCTIWZ x))
+(Cvt32Fto64 x) && objabi.GOPPC == objabi.PpcE6500 -> (Xf2i64 (FCTIDZ x))
+(Cvt64Fto32 x) && objabi.GOPPC == objabi.PpcE6500 -> (Xf2i64 (FCTIWZ x))
+(Cvt64Fto64 x) && objabi.GOPPC == objabi.PpcE6500 -> (Xf2i64 (FCTIDZ x))
(Cvt32to32F x) -> (FCFIDS (MTVSRD (SignExt32to64 x)))
(Cvt32to64F x) -> (FCFID (MTVSRD (SignExt32to64 x)))
@@ -749,8 +749,8 @@
// Use register moves instead of stores and loads to move int<->float values
// Common with math Float64bits, Float64frombits
-(MOVDload [off] {sym} ptr (FMOVDstore [off] {sym} ptr x _)) && !config.OldArch -> (MFVSRD x)
-(FMOVDload [off] {sym} ptr (MOVDstore [off] {sym} ptr x _)) && !config.OldArch -> (MTVSRD x)
+(MOVDload [off] {sym} ptr (FMOVDstore [off] {sym} ptr x _)) && objabi.GOPPC != objabi.PpcE6500 -> (MFVSRD x)
+(FMOVDload [off] {sym} ptr (MOVDstore [off] {sym} ptr x _)) && objabi.GOPPC != objabi.PpcE6500 -> (MTVSRD x)
(FMOVDstore [off] {sym} ptr (MTVSRD x) mem) -> (MOVDstore [off] {sym} ptr x mem)
(MOVDstore [off] {sym} ptr (MFVSRD x) mem) -> (FMOVDstore [off] {sym} ptr x mem)
经过上述修改,我们已经可以通过GOPPC环境变量来控制编译器生成的指令了。
5、遗留问题
经过上面4步的修改,已经可以控制编译器生成的指令了。但是运行时库中的指令还没有控制住。