mirror of
https://github.com/golang/go
synced 2024-10-06 09:11:21 -06:00
[dev.ssa] cmd/internal/ssa: implement more compiler passes
opt: machine-independent optimization fuse: join basic blocks lower: convert to machine-dependent opcodes critical: remove critical edges for register alloc layout: order basic blocks schedule: order values in basic blocks cgen: generate assembly output opt and lower use machine-generated matching rules using the rule generator in rulegen/ cgen will probably change in the real compiler, as we want to generate binary directly instead of ascii assembly. Change-Id: Iedd7ca70f6f55a4cde30e27cfad6a7fa05691b83 Reviewed-on: https://go-review.googlesource.com/7981 Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
7c2c0b4e53
commit
7b96284295
117
src/cmd/internal/ssa/cgen.go
Normal file
117
src/cmd/internal/ssa/cgen.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// cgen selects machine instructions for the function.
|
||||||
|
// This pass generates assembly output for now, but should
|
||||||
|
// TODO(khr): generate binary output (via liblink?) instead of text.
|
||||||
|
func cgen(f *Func) {
|
||||||
|
fmt.Printf("TEXT %s(SB),0,$0\n", f.Name) // TODO: frame size / arg size
|
||||||
|
|
||||||
|
// TODO: prolog, allocate stack frame
|
||||||
|
|
||||||
|
// hack for now, until regalloc is done
|
||||||
|
f.RegAlloc = make([]Location, f.NumValues())
|
||||||
|
|
||||||
|
for idx, b := range f.Blocks {
|
||||||
|
fmt.Printf("%d:\n", b.ID)
|
||||||
|
for _, v := range b.Values {
|
||||||
|
asm := opcodeTable[v.Op].asm
|
||||||
|
fmt.Print("\t")
|
||||||
|
if asm == "" {
|
||||||
|
fmt.Print("\t")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(asm); i++ {
|
||||||
|
switch asm[i] {
|
||||||
|
default:
|
||||||
|
fmt.Printf("%c", asm[i])
|
||||||
|
case '%':
|
||||||
|
i++
|
||||||
|
switch asm[i] {
|
||||||
|
case '%':
|
||||||
|
fmt.Print("%")
|
||||||
|
case 'I':
|
||||||
|
i++
|
||||||
|
n := asm[i] - '0'
|
||||||
|
if f.RegAlloc[v.Args[n].ID] != nil {
|
||||||
|
fmt.Print(f.RegAlloc[v.Args[n].ID].Name())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("v%d", v.Args[n].ID)
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
i++
|
||||||
|
n := asm[i] - '0'
|
||||||
|
if n != 0 {
|
||||||
|
panic("can only handle 1 output for now")
|
||||||
|
}
|
||||||
|
if f.RegAlloc[v.ID] != nil {
|
||||||
|
// TODO: output tuple
|
||||||
|
fmt.Print(f.RegAlloc[v.ID].Name())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("v%d", v.ID)
|
||||||
|
}
|
||||||
|
case 'A':
|
||||||
|
fmt.Print(v.Aux)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("\t; " + v.LongString())
|
||||||
|
}
|
||||||
|
// find next block in layout sequence
|
||||||
|
var next *Block
|
||||||
|
if idx < len(f.Blocks)-1 {
|
||||||
|
next = f.Blocks[idx+1]
|
||||||
|
}
|
||||||
|
// emit end of block code
|
||||||
|
// TODO: this is machine specific
|
||||||
|
switch b.Kind {
|
||||||
|
case BlockPlain:
|
||||||
|
if b.Succs[0] != next {
|
||||||
|
fmt.Printf("\tJMP\t%d\n", b.Succs[0].ID)
|
||||||
|
}
|
||||||
|
case BlockExit:
|
||||||
|
// TODO: run defers (if any)
|
||||||
|
// TODO: deallocate frame
|
||||||
|
fmt.Println("\tRET")
|
||||||
|
case BlockCall:
|
||||||
|
// nothing to emit - call instruction already happened
|
||||||
|
case BlockEQ:
|
||||||
|
if b.Succs[0] == next {
|
||||||
|
fmt.Printf("\tJNE\t%d\n", b.Succs[1].ID)
|
||||||
|
} else if b.Succs[1] == next {
|
||||||
|
fmt.Printf("\tJEQ\t%d\n", b.Succs[0].ID)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\tJEQ\t%d\n", b.Succs[0].ID)
|
||||||
|
fmt.Printf("\tJMP\t%d\n", b.Succs[1].ID)
|
||||||
|
}
|
||||||
|
case BlockNE:
|
||||||
|
if b.Succs[0] == next {
|
||||||
|
fmt.Printf("\tJEQ\t%d\n", b.Succs[1].ID)
|
||||||
|
} else if b.Succs[1] == next {
|
||||||
|
fmt.Printf("\tJNE\t%d\n", b.Succs[0].ID)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\tJNE\t%d\n", b.Succs[0].ID)
|
||||||
|
fmt.Printf("\tJMP\t%d\n", b.Succs[1].ID)
|
||||||
|
}
|
||||||
|
case BlockLT:
|
||||||
|
if b.Succs[0] == next {
|
||||||
|
fmt.Printf("\tJGE\t%d\n", b.Succs[1].ID)
|
||||||
|
} else if b.Succs[1] == next {
|
||||||
|
fmt.Printf("\tJLT\t%d\n", b.Succs[0].ID)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\tJLT\t%d\n", b.Succs[0].ID)
|
||||||
|
fmt.Printf("\tJMP\t%d\n", b.Succs[1].ID)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Printf("\t%s ->", b.Kind.String())
|
||||||
|
for _, s := range b.Succs {
|
||||||
|
fmt.Printf(" %d", s.ID)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
// Compile is the main entry point for this package.
|
// Compile is the main entry point for this package.
|
||||||
// Compile modifies f so that on return:
|
// Compile modifies f so that on return:
|
||||||
@ -50,16 +53,55 @@ type pass struct {
|
|||||||
var passes = [...]pass{
|
var passes = [...]pass{
|
||||||
{"phielim", phielim},
|
{"phielim", phielim},
|
||||||
{"copyelim", copyelim},
|
{"copyelim", copyelim},
|
||||||
//{"opt", opt},
|
{"opt", opt},
|
||||||
// cse
|
// cse
|
||||||
{"deadcode", deadcode},
|
{"deadcode", deadcode},
|
||||||
//{"fuse", fuse},
|
{"fuse", fuse},
|
||||||
//{"lower", lower},
|
{"lower", lower},
|
||||||
// cse
|
// cse
|
||||||
//{"critical", critical}, // remove critical edges
|
{"critical", critical}, // remove critical edges
|
||||||
//{"layout", layout}, // schedule blocks
|
{"layout", layout}, // schedule blocks
|
||||||
//{"schedule", schedule}, // schedule values
|
{"schedule", schedule}, // schedule values
|
||||||
// regalloc
|
// regalloc
|
||||||
// stack slot alloc (+size stack frame)
|
// stack slot alloc (+size stack frame)
|
||||||
//{"cgen", cgen},
|
{"cgen", cgen},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check phase ordering constraints.
|
||||||
|
// This code is intended to document the ordering requirements
|
||||||
|
// between different phases. It does not override the passes
|
||||||
|
// list above.
|
||||||
|
var passOrder = map[string]string{
|
||||||
|
// don't layout blocks until critical edges have been removed
|
||||||
|
"critical": "layout",
|
||||||
|
// regalloc requires the removal of all critical edges
|
||||||
|
//"critical": "regalloc",
|
||||||
|
// regalloc requires all the values in a block to be scheduled
|
||||||
|
//"schedule": "regalloc",
|
||||||
|
// code generation requires register allocation
|
||||||
|
//"cgen":"regalloc",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for a, b := range passOrder {
|
||||||
|
i := -1
|
||||||
|
j := -1
|
||||||
|
for k, p := range passes {
|
||||||
|
if p.name == a {
|
||||||
|
i = k
|
||||||
|
}
|
||||||
|
if p.name == b {
|
||||||
|
j = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < 0 {
|
||||||
|
log.Panicf("pass %s not found", a)
|
||||||
|
}
|
||||||
|
if j < 0 {
|
||||||
|
log.Panicf("pass %s not found", b)
|
||||||
|
}
|
||||||
|
if i >= j {
|
||||||
|
log.Panicf("passes %s and %s out of order", a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
51
src/cmd/internal/ssa/critical.go
Normal file
51
src/cmd/internal/ssa/critical.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
// critical splits critical edges (those that go from a block with
|
||||||
|
// more than one outedge to a block with more than one inedge).
|
||||||
|
// Regalloc wants a critical-edge-free CFG so it can implement phi values.
|
||||||
|
func critical(f *Func) {
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
if len(b.Preds) <= 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// decide if we need to split edges coming into b.
|
||||||
|
hasphi := false
|
||||||
|
for _, v := range b.Values {
|
||||||
|
if v.Op == OpPhi && v.Type != TypeMem {
|
||||||
|
hasphi = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasphi {
|
||||||
|
// no splitting needed
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// split input edges coming from multi-output blocks.
|
||||||
|
for i, c := range b.Preds {
|
||||||
|
if c.Kind == BlockPlain {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a new block to place on the edge
|
||||||
|
d := f.NewBlock(BlockPlain)
|
||||||
|
|
||||||
|
// splice it in
|
||||||
|
d.Preds = append(d.Preds, c)
|
||||||
|
d.Succs = append(d.Succs, b)
|
||||||
|
b.Preds[i] = d
|
||||||
|
// replace b with d in c's successor list.
|
||||||
|
for j, b2 := range c.Succs {
|
||||||
|
if b2 == b {
|
||||||
|
c.Succs[j] = d
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/cmd/internal/ssa/fuse.go
Normal file
40
src/cmd/internal/ssa/fuse.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
// fuse simplifies control flow by joining basic blocks.
|
||||||
|
func fuse(f *Func) {
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
if b.Kind != BlockPlain {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c := b.Succs[0]
|
||||||
|
if len(c.Preds) != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// move all of b's values to c.
|
||||||
|
for _, v := range b.Values {
|
||||||
|
v.Block = c
|
||||||
|
c.Values = append(c.Values, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace b->c edge with preds(b) -> c
|
||||||
|
c.Preds = b.Preds
|
||||||
|
for _, p := range c.Preds {
|
||||||
|
for i, q := range p.Succs {
|
||||||
|
if q == b {
|
||||||
|
p.Succs[i] = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trash b, just in case
|
||||||
|
b.Kind = BlockUnknown
|
||||||
|
b.Values = nil
|
||||||
|
b.Preds = nil
|
||||||
|
b.Succs = nil
|
||||||
|
}
|
||||||
|
}
|
111
src/cmd/internal/ssa/generic.go
Normal file
111
src/cmd/internal/ssa/generic.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// autogenerated from rulegen/generic.rules: do not edit!
|
||||||
|
// generated with: go run rulegen/rulegen.go rulegen/generic.rules genericRules generic.go
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
func genericRules(v *Value) bool {
|
||||||
|
switch v.Op {
|
||||||
|
case OpAdd:
|
||||||
|
// match: (Add <t> (ConstInt [c]) (ConstInt [d]))
|
||||||
|
// cond: is64BitInt(t)
|
||||||
|
// result: (ConstInt [{c.(int64)+d.(int64)}])
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
if v.Args[0].Op != OpConstInt {
|
||||||
|
goto end0
|
||||||
|
}
|
||||||
|
c := v.Args[0].Aux
|
||||||
|
if v.Args[1].Op != OpConstInt {
|
||||||
|
goto end0
|
||||||
|
}
|
||||||
|
d := v.Args[1].Aux
|
||||||
|
if !(is64BitInt(t)) {
|
||||||
|
goto end0
|
||||||
|
}
|
||||||
|
v.Op = OpConstInt
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = c.(int64) + d.(int64)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end0:
|
||||||
|
;
|
||||||
|
case OpLoad:
|
||||||
|
// match: (Load (FPAddr [offset]) mem)
|
||||||
|
// cond:
|
||||||
|
// result: (LoadFP [offset] mem)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpFPAddr {
|
||||||
|
goto end1
|
||||||
|
}
|
||||||
|
offset := v.Args[0].Aux
|
||||||
|
mem := v.Args[1]
|
||||||
|
v.Op = OpLoadFP
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end1:
|
||||||
|
;
|
||||||
|
// match: (Load (SPAddr [offset]) mem)
|
||||||
|
// cond:
|
||||||
|
// result: (LoadSP [offset] mem)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpSPAddr {
|
||||||
|
goto end2
|
||||||
|
}
|
||||||
|
offset := v.Args[0].Aux
|
||||||
|
mem := v.Args[1]
|
||||||
|
v.Op = OpLoadSP
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end2:
|
||||||
|
;
|
||||||
|
case OpStore:
|
||||||
|
// match: (Store (FPAddr [offset]) val mem)
|
||||||
|
// cond:
|
||||||
|
// result: (StoreFP [offset] val mem)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpFPAddr {
|
||||||
|
goto end3
|
||||||
|
}
|
||||||
|
offset := v.Args[0].Aux
|
||||||
|
val := v.Args[1]
|
||||||
|
mem := v.Args[2]
|
||||||
|
v.Op = OpStoreFP
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end3:
|
||||||
|
;
|
||||||
|
// match: (Store (SPAddr [offset]) val mem)
|
||||||
|
// cond:
|
||||||
|
// result: (StoreSP [offset] val mem)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpSPAddr {
|
||||||
|
goto end4
|
||||||
|
}
|
||||||
|
offset := v.Args[0].Aux
|
||||||
|
val := v.Args[1]
|
||||||
|
mem := v.Args[2]
|
||||||
|
v.Op = OpStoreSP
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end4:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
88
src/cmd/internal/ssa/layout.go
Normal file
88
src/cmd/internal/ssa/layout.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
// layout orders basic blocks in f with the goal of minimizing control flow instructions.
|
||||||
|
// After this phase returns, the order of f.Blocks matters and is the order
|
||||||
|
// in which those blocks will appear in the assembly output.
|
||||||
|
func layout(f *Func) {
|
||||||
|
order := make([]*Block, 0, f.NumBlocks())
|
||||||
|
scheduled := make([]bool, f.NumBlocks())
|
||||||
|
idToBlock := make([]*Block, f.NumBlocks())
|
||||||
|
indegree := make([]int, f.NumBlocks())
|
||||||
|
posdegree := newSparseSet(f.NumBlocks()) // blocks with positive remaining degree
|
||||||
|
zerodegree := newSparseSet(f.NumBlocks()) // blocks with zero remaining degree
|
||||||
|
|
||||||
|
// Initialize indegree of each block
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
idToBlock[b.ID] = b
|
||||||
|
indegree[b.ID] = len(b.Preds)
|
||||||
|
if len(b.Preds) == 0 {
|
||||||
|
zerodegree.add(b.ID)
|
||||||
|
} else {
|
||||||
|
posdegree.add(b.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bid := f.Entry.ID
|
||||||
|
blockloop:
|
||||||
|
for {
|
||||||
|
// add block to schedule
|
||||||
|
b := idToBlock[bid]
|
||||||
|
order = append(order, b)
|
||||||
|
scheduled[bid] = true
|
||||||
|
if len(order) == len(f.Blocks) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range b.Succs {
|
||||||
|
indegree[c.ID]--
|
||||||
|
if indegree[c.ID] == 0 {
|
||||||
|
posdegree.remove(c.ID)
|
||||||
|
zerodegree.add(c.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick the next block to schedule
|
||||||
|
// Pick among the successor blocks that have not been scheduled yet.
|
||||||
|
// Just use degree for now. TODO(khr): use likely direction hints.
|
||||||
|
bid = 0
|
||||||
|
mindegree := f.NumBlocks()
|
||||||
|
for _, c := range order[len(order)-1].Succs {
|
||||||
|
if scheduled[c.ID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if indegree[c.ID] < mindegree {
|
||||||
|
mindegree = indegree[c.ID]
|
||||||
|
bid = c.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bid != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO: improve this part
|
||||||
|
// No successor of the previously scheduled block works.
|
||||||
|
// Pick a zero-degree block if we can.
|
||||||
|
for zerodegree.size() > 0 {
|
||||||
|
cid := zerodegree.pop()
|
||||||
|
if !scheduled[cid] {
|
||||||
|
bid = cid
|
||||||
|
continue blockloop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Still nothing, pick any block.
|
||||||
|
for {
|
||||||
|
cid := posdegree.pop()
|
||||||
|
if !scheduled[cid] {
|
||||||
|
bid = cid
|
||||||
|
continue blockloop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Panicf("no block available for layout")
|
||||||
|
}
|
||||||
|
f.Blocks = order
|
||||||
|
}
|
43
src/cmd/internal/ssa/lower.go
Normal file
43
src/cmd/internal/ssa/lower.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
var (
|
||||||
|
// TODO(khr): put arch configuration constants together somewhere
|
||||||
|
intSize = 8
|
||||||
|
ptrSize = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run rulegen/rulegen.go rulegen/lower_amd64.rules lowerAmd64 lowerAmd64.go
|
||||||
|
|
||||||
|
// convert to machine-dependent ops
|
||||||
|
func lower(f *Func) {
|
||||||
|
// repeat rewrites until we find no more rewrites
|
||||||
|
// TODO: pick the target arch from config
|
||||||
|
applyRewrite(f, lowerAmd64)
|
||||||
|
|
||||||
|
// TODO: check for unlowered opcodes, fail if we find one
|
||||||
|
|
||||||
|
// additional pass for 386/amd64, link condition codes directly to blocks
|
||||||
|
// TODO: do generically somehow? Special "block" rewrite rules?
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
switch b.Kind {
|
||||||
|
case BlockIf:
|
||||||
|
switch b.Control.Op {
|
||||||
|
case OpSETL:
|
||||||
|
b.Kind = BlockLT
|
||||||
|
b.Control = b.Control.Args[0]
|
||||||
|
// TODO: others
|
||||||
|
}
|
||||||
|
case BlockLT:
|
||||||
|
if b.Control.Op == OpInvertFlags {
|
||||||
|
b.Kind = BlockGE
|
||||||
|
b.Control = b.Control.Args[0]
|
||||||
|
}
|
||||||
|
// TODO: others
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deadcode(f) // TODO: separate pass?
|
||||||
|
}
|
307
src/cmd/internal/ssa/lowerAmd64.go
Normal file
307
src/cmd/internal/ssa/lowerAmd64.go
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
// autogenerated from rulegen/lower_amd64.rules: do not edit!
|
||||||
|
// generated with: go run rulegen/rulegen.go rulegen/lower_amd64.rules lowerAmd64 lowerAmd64.go
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
func lowerAmd64(v *Value) bool {
|
||||||
|
switch v.Op {
|
||||||
|
case OpADDQ:
|
||||||
|
// match: (ADDQ x (ConstInt [c]))
|
||||||
|
// cond:
|
||||||
|
// result: (ADDCQ [c] x)
|
||||||
|
{
|
||||||
|
x := v.Args[0]
|
||||||
|
if v.Args[1].Op != OpConstInt {
|
||||||
|
goto end0
|
||||||
|
}
|
||||||
|
c := v.Args[1].Aux
|
||||||
|
v.Op = OpADDCQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = c
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end0:
|
||||||
|
;
|
||||||
|
// match: (ADDQ (ConstInt [c]) x)
|
||||||
|
// cond:
|
||||||
|
// result: (ADDCQ [c] x)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpConstInt {
|
||||||
|
goto end1
|
||||||
|
}
|
||||||
|
c := v.Args[0].Aux
|
||||||
|
x := v.Args[1]
|
||||||
|
v.Op = OpADDCQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = c
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end1:
|
||||||
|
;
|
||||||
|
case OpAdd:
|
||||||
|
// match: (Add <t> x y)
|
||||||
|
// cond: is64BitInt(t)
|
||||||
|
// result: (ADDQ x y)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
if !(is64BitInt(t)) {
|
||||||
|
goto end2
|
||||||
|
}
|
||||||
|
v.Op = OpADDQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
v.AddArg(y)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end2:
|
||||||
|
;
|
||||||
|
// match: (Add <t> x y)
|
||||||
|
// cond: is32BitInt(t)
|
||||||
|
// result: (ADDL x y)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
if !(is32BitInt(t)) {
|
||||||
|
goto end3
|
||||||
|
}
|
||||||
|
v.Op = OpADDL
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
v.AddArg(y)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end3:
|
||||||
|
;
|
||||||
|
case OpCMPQ:
|
||||||
|
// match: (CMPQ x (ConstInt [c]))
|
||||||
|
// cond:
|
||||||
|
// result: (CMPCQ x [c])
|
||||||
|
{
|
||||||
|
x := v.Args[0]
|
||||||
|
if v.Args[1].Op != OpConstInt {
|
||||||
|
goto end4
|
||||||
|
}
|
||||||
|
c := v.Args[1].Aux
|
||||||
|
v.Op = OpCMPCQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
v.Aux = c
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end4:
|
||||||
|
;
|
||||||
|
// match: (CMPQ (ConstInt [c]) x)
|
||||||
|
// cond:
|
||||||
|
// result: (InvertFlags (CMPCQ x [c]))
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpConstInt {
|
||||||
|
goto end5
|
||||||
|
}
|
||||||
|
c := v.Args[0].Aux
|
||||||
|
x := v.Args[1]
|
||||||
|
v.Op = OpInvertFlags
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v0 := v.Block.NewValue(OpCMPCQ, TypeInvalid, nil)
|
||||||
|
v0.AddArg(x)
|
||||||
|
v0.Aux = c
|
||||||
|
v0.SetType()
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end5:
|
||||||
|
;
|
||||||
|
case OpLess:
|
||||||
|
// match: (Less x y)
|
||||||
|
// cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)
|
||||||
|
// result: (SETL (CMPQ x y))
|
||||||
|
{
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) {
|
||||||
|
goto end6
|
||||||
|
}
|
||||||
|
v.Op = OpSETL
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil)
|
||||||
|
v0.AddArg(x)
|
||||||
|
v0.AddArg(y)
|
||||||
|
v0.SetType()
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end6:
|
||||||
|
;
|
||||||
|
case OpLoadFP:
|
||||||
|
// match: (LoadFP <t> [offset] mem)
|
||||||
|
// cond: typeSize(t) == 8
|
||||||
|
// result: (LoadFP8 <t> [offset] mem)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
offset := v.Aux
|
||||||
|
mem := v.Args[0]
|
||||||
|
if !(typeSize(t) == 8) {
|
||||||
|
goto end7
|
||||||
|
}
|
||||||
|
v.Op = OpLoadFP8
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Type = t
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end7:
|
||||||
|
;
|
||||||
|
case OpLoadSP:
|
||||||
|
// match: (LoadSP <t> [offset] mem)
|
||||||
|
// cond: typeSize(t) == 8
|
||||||
|
// result: (LoadSP8 <t> [offset] mem)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
offset := v.Aux
|
||||||
|
mem := v.Args[0]
|
||||||
|
if !(typeSize(t) == 8) {
|
||||||
|
goto end8
|
||||||
|
}
|
||||||
|
v.Op = OpLoadSP8
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Type = t
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end8:
|
||||||
|
;
|
||||||
|
case OpSETL:
|
||||||
|
// match: (SETL (InvertFlags x))
|
||||||
|
// cond:
|
||||||
|
// result: (SETGE x)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpInvertFlags {
|
||||||
|
goto end9
|
||||||
|
}
|
||||||
|
x := v.Args[0].Args[0]
|
||||||
|
v.Op = OpSETGE
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end9:
|
||||||
|
;
|
||||||
|
case OpSUBQ:
|
||||||
|
// match: (SUBQ x (ConstInt [c]))
|
||||||
|
// cond:
|
||||||
|
// result: (SUBCQ x [c])
|
||||||
|
{
|
||||||
|
x := v.Args[0]
|
||||||
|
if v.Args[1].Op != OpConstInt {
|
||||||
|
goto end10
|
||||||
|
}
|
||||||
|
c := v.Args[1].Aux
|
||||||
|
v.Op = OpSUBCQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
v.Aux = c
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end10:
|
||||||
|
;
|
||||||
|
// match: (SUBQ (ConstInt [c]) x)
|
||||||
|
// cond:
|
||||||
|
// result: (NEGQ (SUBCQ x [c]))
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpConstInt {
|
||||||
|
goto end11
|
||||||
|
}
|
||||||
|
c := v.Args[0].Aux
|
||||||
|
x := v.Args[1]
|
||||||
|
v.Op = OpNEGQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v0 := v.Block.NewValue(OpSUBCQ, TypeInvalid, nil)
|
||||||
|
v0.AddArg(x)
|
||||||
|
v0.Aux = c
|
||||||
|
v0.SetType()
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end11:
|
||||||
|
;
|
||||||
|
case OpStoreFP:
|
||||||
|
// match: (StoreFP [offset] val mem)
|
||||||
|
// cond: typeSize(val.Type) == 8
|
||||||
|
// result: (StoreFP8 [offset] val mem)
|
||||||
|
{
|
||||||
|
offset := v.Aux
|
||||||
|
val := v.Args[0]
|
||||||
|
mem := v.Args[1]
|
||||||
|
if !(typeSize(val.Type) == 8) {
|
||||||
|
goto end12
|
||||||
|
}
|
||||||
|
v.Op = OpStoreFP8
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end12:
|
||||||
|
;
|
||||||
|
case OpStoreSP:
|
||||||
|
// match: (StoreSP [offset] val mem)
|
||||||
|
// cond: typeSize(val.Type) == 8
|
||||||
|
// result: (StoreSP8 [offset] val mem)
|
||||||
|
{
|
||||||
|
offset := v.Aux
|
||||||
|
val := v.Args[0]
|
||||||
|
mem := v.Args[1]
|
||||||
|
if !(typeSize(val.Type) == 8) {
|
||||||
|
goto end13
|
||||||
|
}
|
||||||
|
v.Op = OpStoreSP8
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.Aux = offset
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end13:
|
||||||
|
;
|
||||||
|
case OpSub:
|
||||||
|
// match: (Sub <t> x y)
|
||||||
|
// cond: is64BitInt(t)
|
||||||
|
// result: (SUBQ x y)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
if !(is64BitInt(t)) {
|
||||||
|
goto end14
|
||||||
|
}
|
||||||
|
v.Op = OpSUBQ
|
||||||
|
v.Aux = nil
|
||||||
|
v.Args = v.argstorage[:0]
|
||||||
|
v.AddArg(x)
|
||||||
|
v.AddArg(y)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
end14:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -62,7 +62,9 @@ const (
|
|||||||
OpCheckBound // 0 <= arg[0] < arg[1]
|
OpCheckBound // 0 <= arg[0] < arg[1]
|
||||||
|
|
||||||
// function calls. Arguments to the call have already been written to the stack.
|
// function calls. Arguments to the call have already been written to the stack.
|
||||||
// Return values appear on the stack.
|
// Return values appear on the stack. The method receiver, if any, is treated
|
||||||
|
// as a phantom first argument.
|
||||||
|
// TODO: closure pointer must be in a register.
|
||||||
OpCall // args are function ptr, memory
|
OpCall // args are function ptr, memory
|
||||||
OpStaticCall // aux is function, arg is memory
|
OpStaticCall // aux is function, arg is memory
|
||||||
|
|
||||||
@ -82,33 +84,38 @@ const (
|
|||||||
OpStoreFP
|
OpStoreFP
|
||||||
OpStoreSP
|
OpStoreSP
|
||||||
|
|
||||||
// spill and restore ops for the register allocator. These are
|
// spill&restore ops for the register allocator. These are
|
||||||
// semantically identical to OpCopy - they do not take/return
|
// semantically identical to OpCopy; they do not take/return
|
||||||
// stores like regular memory ops do. We can get away with that because
|
// stores like regular memory ops do. We can get away without memory
|
||||||
// we know there is no aliasing to spill slots on the stack.
|
// args because we know there is no aliasing of spill slots on the stack.
|
||||||
OpStoreReg8
|
OpStoreReg8
|
||||||
OpLoadReg8
|
OpLoadReg8
|
||||||
|
|
||||||
// machine-dependent opcodes go here
|
// machine-dependent opcodes go here
|
||||||
|
|
||||||
// x86
|
// amd64
|
||||||
OpADDQ
|
OpADDQ
|
||||||
OpSUBQ
|
OpSUBQ
|
||||||
OpADDCQ // 1 input arg, add aux which is an int64 constant
|
OpADDCQ // 1 input arg. output = input + aux.(int64)
|
||||||
OpSUBCQ // 1 input arg. output = input - aux.(int64)
|
OpSUBCQ // 1 input arg. output = input - aux.(int64)
|
||||||
OpNEGQ
|
OpNEGQ
|
||||||
OpCMPQ
|
OpCMPQ
|
||||||
OpCMPCQ // 1 input arg. Compares input with aux.(int64)
|
OpCMPCQ // 1 input arg. Compares input with aux.(int64)
|
||||||
OpADDL
|
OpADDL
|
||||||
OpInvertFlags // inverts interpretation of the flags register (< to >=, etc.)
|
OpSETL // generate bool = "flags encode less than"
|
||||||
OpSETL // generate bool = "flags encode less than"
|
|
||||||
OpSETGE
|
OpSETGE
|
||||||
|
|
||||||
|
// InvertFlags reverses direction of flags register interpretation:
|
||||||
|
// (InvertFlags (OpCMPQ a b)) == (OpCMPQ b a)
|
||||||
|
// This is a pseudo-op which can't appear in assembly output.
|
||||||
|
OpInvertFlags
|
||||||
|
|
||||||
OpLEAQ // x+y
|
OpLEAQ // x+y
|
||||||
OpLEAQ2 // x+2*y
|
OpLEAQ2 // x+2*y
|
||||||
OpLEAQ4 // x+4*y
|
OpLEAQ4 // x+4*y
|
||||||
OpLEAQ8 // x+8*y
|
OpLEAQ8 // x+8*y
|
||||||
|
|
||||||
|
// load/store 8-byte integer register from stack slot.
|
||||||
OpLoadFP8
|
OpLoadFP8
|
||||||
OpLoadSP8
|
OpLoadSP8
|
||||||
OpStoreFP8
|
OpStoreFP8
|
||||||
|
@ -4,9 +4,9 @@ package ssa
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstNilOpConstBoolOpConstStringOpConstIntOpConstFloatOpConstComplexOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpInvertFlagsOpSETLOpSETGEOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
|
const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstNilOpConstBoolOpConstStringOpConstIntOpConstFloatOpConstComplexOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpSETLOpSETGEOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
|
||||||
|
|
||||||
var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 52, 63, 76, 86, 98, 112, 117, 125, 131, 137, 142, 153, 163, 173, 183, 195, 206, 217, 224, 231, 242, 248, 255, 265, 277, 283, 295, 304, 313, 321, 329, 337, 345, 354, 363, 374, 384, 390, 396, 403, 410, 416, 422, 429, 435, 448, 454, 461, 467, 474, 481, 488, 497, 506, 516, 526, 531}
|
var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 52, 63, 76, 86, 98, 112, 117, 125, 131, 137, 142, 153, 163, 173, 183, 195, 206, 217, 224, 231, 242, 248, 255, 265, 277, 283, 295, 304, 313, 321, 329, 337, 345, 354, 363, 374, 384, 390, 396, 403, 410, 416, 422, 429, 435, 441, 448, 461, 467, 474, 481, 488, 497, 506, 516, 526, 531}
|
||||||
|
|
||||||
func (i Op) String() string {
|
func (i Op) String() string {
|
||||||
if i < 0 || i+1 >= Op(len(_Op_index)) {
|
if i < 0 || i+1 >= Op(len(_Op_index)) {
|
||||||
|
13
src/cmd/internal/ssa/opt.go
Normal file
13
src/cmd/internal/ssa/opt.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
// machine-independent optimization
|
||||||
|
|
||||||
|
//go:generate go run rulegen/rulegen.go rulegen/generic.rules genericRules generic.go
|
||||||
|
|
||||||
|
func opt(f *Func) {
|
||||||
|
applyRewrite(f, genericRules)
|
||||||
|
}
|
70
src/cmd/internal/ssa/rewrite.go
Normal file
70
src/cmd/internal/ssa/rewrite.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/internal/ssa/types" // TODO: use golang.org/x/tools/go/types instead
|
||||||
|
)
|
||||||
|
|
||||||
|
func applyRewrite(f *Func, r func(*Value) bool) {
|
||||||
|
// repeat rewrites until we find no more rewrites
|
||||||
|
for {
|
||||||
|
change := false
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
for _, v := range b.Values {
|
||||||
|
if r(v) {
|
||||||
|
change = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !change {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common functions called from rewriting rules
|
||||||
|
|
||||||
|
func is64BitInt(t Type) bool {
|
||||||
|
return typeIdentical(t, TypeInt64) ||
|
||||||
|
typeIdentical(t, TypeUint64) ||
|
||||||
|
(typeIdentical(t, TypeInt) && intSize == 8) ||
|
||||||
|
(typeIdentical(t, TypeUint) && intSize == 8) ||
|
||||||
|
(typeIdentical(t, TypeUintptr) && ptrSize == 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func is32BitInt(t Type) bool {
|
||||||
|
return typeIdentical(t, TypeInt32) ||
|
||||||
|
typeIdentical(t, TypeUint32) ||
|
||||||
|
(typeIdentical(t, TypeInt) && intSize == 4) ||
|
||||||
|
(typeIdentical(t, TypeUint) && intSize == 4) ||
|
||||||
|
(typeIdentical(t, TypeUintptr) && ptrSize == 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSigned(t Type) bool {
|
||||||
|
return typeIdentical(t, TypeInt) ||
|
||||||
|
typeIdentical(t, TypeInt8) ||
|
||||||
|
typeIdentical(t, TypeInt16) ||
|
||||||
|
typeIdentical(t, TypeInt32) ||
|
||||||
|
typeIdentical(t, TypeInt64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeSize(t Type) int {
|
||||||
|
switch t {
|
||||||
|
case TypeInt32, TypeUint32:
|
||||||
|
return 4
|
||||||
|
case TypeInt64, TypeUint64:
|
||||||
|
return 8
|
||||||
|
case TypeUintptr:
|
||||||
|
return ptrSize
|
||||||
|
case TypeInt, TypeUint:
|
||||||
|
return intSize
|
||||||
|
default:
|
||||||
|
if _, ok := t.(*types.Pointer); ok {
|
||||||
|
return ptrSize
|
||||||
|
}
|
||||||
|
panic("TODO: width of " + t.String())
|
||||||
|
}
|
||||||
|
}
|
16
src/cmd/internal/ssa/rulegen/generic.rules
Normal file
16
src/cmd/internal/ssa/rulegen/generic.rules
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// constant folding
|
||||||
|
(Add <t> (ConstInt [c]) (ConstInt [d])) && is64BitInt(t) -> (ConstInt [{c.(int64)+d.(int64)}])
|
||||||
|
|
||||||
|
// load/store to stack
|
||||||
|
(Load (FPAddr [offset]) mem) -> (LoadFP [offset] mem)
|
||||||
|
(Store (FPAddr [offset]) val mem) -> (StoreFP [offset] val mem)
|
||||||
|
|
||||||
|
(Load (SPAddr [offset]) mem) -> (LoadSP [offset] mem)
|
||||||
|
(Store (SPAddr [offset]) val mem) -> (StoreSP [offset] val mem)
|
||||||
|
|
||||||
|
// expand array indexing
|
||||||
|
// others? Depends on what is already done by frontend
|
46
src/cmd/internal/ssa/rulegen/lower_amd64.rules
Normal file
46
src/cmd/internal/ssa/rulegen/lower_amd64.rules
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// values are specified using the following format:
|
||||||
|
// (op <type> [aux] arg0 arg1 ...)
|
||||||
|
// the type and aux fields are optional
|
||||||
|
// on the matching side
|
||||||
|
// - the types and aux fields must match if they are specified.
|
||||||
|
// on the generated side
|
||||||
|
// - types will be computed by opcode typers if not specified explicitly.
|
||||||
|
// - aux will be nil if not specified.
|
||||||
|
|
||||||
|
// x86 register conventions:
|
||||||
|
// - Integer types live in the low portion of registers. Upper portions are junk.
|
||||||
|
// - Boolean types use the low-order byte of a register. Upper bytes are junk.
|
||||||
|
// - We do not use AH,BH,CH,DH registers.
|
||||||
|
// - Floating-point types will live in the low natural slot of an sse2 register.
|
||||||
|
// Unused portions are junk.
|
||||||
|
|
||||||
|
// These are the lowerings themselves
|
||||||
|
(Add <t> x y) && is64BitInt(t) -> (ADDQ x y)
|
||||||
|
(Add <t> x y) && is32BitInt(t) -> (ADDL x y)
|
||||||
|
|
||||||
|
(Sub <t> x y) && is64BitInt(t) -> (SUBQ x y)
|
||||||
|
|
||||||
|
(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y))
|
||||||
|
|
||||||
|
// stack loads/stores
|
||||||
|
(LoadFP <t> [offset] mem) && typeSize(t) == 8 -> (LoadFP8 <t> [offset] mem)
|
||||||
|
(StoreFP [offset] val mem) && typeSize(val.Type) == 8 -> (StoreFP8 [offset] val mem)
|
||||||
|
(LoadSP <t> [offset] mem) && typeSize(t) == 8 -> (LoadSP8 <t> [offset] mem)
|
||||||
|
(StoreSP [offset] val mem) && typeSize(val.Type) == 8 -> (StoreSP8 [offset] val mem)
|
||||||
|
|
||||||
|
// Rules below here apply some simple optimizations after lowering.
|
||||||
|
// TODO: Should this be a separate pass?
|
||||||
|
|
||||||
|
(ADDQ x (ConstInt [c])) -> (ADDCQ [c] x) // TODO: restrict c to int32 range?
|
||||||
|
(ADDQ (ConstInt [c]) x) -> (ADDCQ [c] x)
|
||||||
|
(SUBQ x (ConstInt [c])) -> (SUBCQ x [c])
|
||||||
|
(SUBQ (ConstInt [c]) x) -> (NEGQ (SUBCQ x [c]))
|
||||||
|
(CMPQ x (ConstInt [c])) -> (CMPCQ x [c])
|
||||||
|
(CMPQ (ConstInt [c]) x) -> (InvertFlags (CMPCQ x [c]))
|
||||||
|
|
||||||
|
// reverse ordering of compare instruction
|
||||||
|
(SETL (InvertFlags x)) -> (SETGE x)
|
328
src/cmd/internal/ssa/rulegen/rulegen.go
Normal file
328
src/cmd/internal/ssa/rulegen/rulegen.go
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This program generates Go code that applies rewrite rules to a Value.
|
||||||
|
// The generated code implements a function of type func (v *Value) bool
|
||||||
|
// which returns true iff if did something.
|
||||||
|
// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html
|
||||||
|
|
||||||
|
// Run with something like "go run rulegen.go lower_amd64.rules lowerAmd64 lowerAmd64.go"
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rule syntax:
|
||||||
|
// sexpr [&& extra conditions] -> sexpr
|
||||||
|
//
|
||||||
|
// sexpr are s-expressions (lisp-like parenthesized groupings)
|
||||||
|
// sexpr ::= (opcode sexpr*)
|
||||||
|
// | variable
|
||||||
|
// | [aux]
|
||||||
|
// | <type>
|
||||||
|
// | {code}
|
||||||
|
//
|
||||||
|
// aux ::= variable | {code}
|
||||||
|
// type ::= variable | {code}
|
||||||
|
// variable ::= some token
|
||||||
|
// opcode ::= one of the opcodes from ../op.go (without the Op prefix)
|
||||||
|
|
||||||
|
// extra conditions is just a chunk of Go that evaluates to a boolean. It may use
|
||||||
|
// variables declared in the matching sexpr. The variable "v" is predefined to be
|
||||||
|
// the value matched by the entire rule.
|
||||||
|
|
||||||
|
// If multiple rules match, the first one in file order is selected.
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 3 || len(os.Args) > 4 {
|
||||||
|
fmt.Printf("usage: go run rulegen.go <rule file> <function name> [<output file>]")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
rulefile := os.Args[1]
|
||||||
|
rulefn := os.Args[2]
|
||||||
|
|
||||||
|
// Open input file.
|
||||||
|
text, err := os.Open(rulefile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't read rule file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// oprules contains a list of rules for each opcode
|
||||||
|
oprules := map[string][]string{}
|
||||||
|
|
||||||
|
// read rule file
|
||||||
|
scanner := bufio.NewScanner(text)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if i := strings.Index(line, "//"); i >= 0 {
|
||||||
|
// Remove comments. Note that this isn't string safe, so
|
||||||
|
// it will truncate lines with // inside strings. Oh well.
|
||||||
|
line = line[:i]
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
op := strings.Split(line, " ")[0][1:]
|
||||||
|
oprules[op] = append(oprules[op], line)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Fatalf("scanner failed: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start output buffer, write header.
|
||||||
|
w := new(bytes.Buffer)
|
||||||
|
fmt.Fprintf(w, "// autogenerated from %s: do not edit!\n", rulefile)
|
||||||
|
fmt.Fprintf(w, "// generated with: go run rulegen/rulegen.go %s\n", strings.Join(os.Args[1:], " "))
|
||||||
|
fmt.Fprintln(w, "package ssa")
|
||||||
|
fmt.Fprintf(w, "func %s(v *Value) bool {\n", rulefn)
|
||||||
|
|
||||||
|
// generate code for each rule
|
||||||
|
fmt.Fprintf(w, "switch v.Op {\n")
|
||||||
|
var ops []string
|
||||||
|
for op := range oprules {
|
||||||
|
ops = append(ops, op)
|
||||||
|
}
|
||||||
|
sort.Strings(ops)
|
||||||
|
rulenum := 0
|
||||||
|
for _, op := range ops {
|
||||||
|
fmt.Fprintf(w, "case Op%s:\n", op)
|
||||||
|
for _, rule := range oprules[op] {
|
||||||
|
// split at ->
|
||||||
|
s := strings.Split(rule, "->")
|
||||||
|
if len(s) != 2 {
|
||||||
|
log.Fatalf("no arrow in rule %s", rule)
|
||||||
|
}
|
||||||
|
lhs := strings.Trim(s[0], " \t")
|
||||||
|
result := strings.Trim(s[1], " \t\n")
|
||||||
|
|
||||||
|
// split match into matching part and additional condition
|
||||||
|
match := lhs
|
||||||
|
cond := ""
|
||||||
|
if i := strings.Index(match, "&&"); i >= 0 {
|
||||||
|
cond = strings.Trim(match[i+2:], " \t")
|
||||||
|
match = strings.Trim(match[:i], " \t")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "// match: %s\n", match)
|
||||||
|
fmt.Fprintf(w, "// cond: %s\n", cond)
|
||||||
|
fmt.Fprintf(w, "// result: %s\n", result)
|
||||||
|
|
||||||
|
fail := fmt.Sprintf("{\ngoto end%d\n}\n", rulenum)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "{\n")
|
||||||
|
genMatch(w, match, fail)
|
||||||
|
|
||||||
|
if cond != "" {
|
||||||
|
fmt.Fprintf(w, "if !(%s) %s", cond, fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
genResult(w, result)
|
||||||
|
fmt.Fprintf(w, "return true\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "}\n")
|
||||||
|
fmt.Fprintf(w, "end%d:;\n", rulenum)
|
||||||
|
rulenum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "}\n")
|
||||||
|
fmt.Fprintf(w, "return false\n")
|
||||||
|
fmt.Fprintf(w, "}\n")
|
||||||
|
|
||||||
|
// gofmt result
|
||||||
|
b := w.Bytes()
|
||||||
|
b, err = format.Source(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to a file if given, otherwise stdout.
|
||||||
|
var out io.WriteCloser
|
||||||
|
if len(os.Args) >= 4 {
|
||||||
|
outfile := os.Args[3]
|
||||||
|
out, err = os.Create(outfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't open output file %s: %v\n", outfile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out = os.Stdout
|
||||||
|
}
|
||||||
|
if _, err = out.Write(b); err != nil {
|
||||||
|
log.Fatalf("can't write output: %v\n", err)
|
||||||
|
}
|
||||||
|
if err = out.Close(); err != nil {
|
||||||
|
log.Fatalf("can't close output: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMatch(w io.Writer, match, fail string) {
|
||||||
|
genMatch0(w, match, "v", fail, map[string]string{}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMatch0(w io.Writer, match, v, fail string, m map[string]string, top bool) {
|
||||||
|
if match[0] != '(' {
|
||||||
|
if x, ok := m[match]; ok {
|
||||||
|
// variable already has a definition. Check whether
|
||||||
|
// the old definition and the new definition match.
|
||||||
|
// For example, (add x x). Equality is just pointer equality
|
||||||
|
// on Values (so cse is important to do before lowering).
|
||||||
|
fmt.Fprintf(w, "if %s != %s %s", v, x, fail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// remember that this variable references the given value
|
||||||
|
m[match] = v
|
||||||
|
fmt.Fprintf(w, "%s := %s\n", match, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// split body up into regions. Split by spaces/tabs, except those
|
||||||
|
// contained in () or {}.
|
||||||
|
s := split(match[1 : len(match)-1])
|
||||||
|
|
||||||
|
// check op
|
||||||
|
if !top {
|
||||||
|
fmt.Fprintf(w, "if %s.Op != Op%s %s", v, s[0], fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check type/aux/args
|
||||||
|
argnum := 0
|
||||||
|
for _, a := range s[1:] {
|
||||||
|
if a[0] == '<' {
|
||||||
|
// type restriction
|
||||||
|
t := a[1 : len(a)-1]
|
||||||
|
if t[0] == '{' {
|
||||||
|
// code. We must match the results of this code.
|
||||||
|
fmt.Fprintf(w, "if %s.Type != %s %s", v, t[1:len(t)-1], fail)
|
||||||
|
} else {
|
||||||
|
// variable
|
||||||
|
if u, ok := m[t]; ok {
|
||||||
|
// must match previous variable
|
||||||
|
fmt.Fprintf(w, "if %s.Type != %s %s", v, u, fail)
|
||||||
|
} else {
|
||||||
|
m[t] = v + ".Type"
|
||||||
|
fmt.Fprintf(w, "%s := %s.Type\n", t, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if a[0] == '[' {
|
||||||
|
// aux restriction
|
||||||
|
x := a[1 : len(a)-1]
|
||||||
|
if x[0] == '{' {
|
||||||
|
// code
|
||||||
|
fmt.Fprintf(w, "if %s.Aux != %s %s", v, x[1:len(x)-1], fail)
|
||||||
|
} else {
|
||||||
|
// variable
|
||||||
|
if y, ok := m[x]; ok {
|
||||||
|
fmt.Fprintf(w, "if %s.Aux != %s %s", v, y, fail)
|
||||||
|
} else {
|
||||||
|
m[x] = v + ".Aux"
|
||||||
|
fmt.Fprintf(w, "%s := %s.Aux\n", x, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if a[0] == '{' {
|
||||||
|
fmt.Fprintf(w, "if %s.Args[%d] != %s %s", v, argnum, a[1:len(a)-1], fail)
|
||||||
|
argnum++
|
||||||
|
} else {
|
||||||
|
// variable or sexpr
|
||||||
|
genMatch0(w, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false)
|
||||||
|
argnum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genResult(w io.Writer, result string) {
|
||||||
|
genResult0(w, result, new(int), true)
|
||||||
|
}
|
||||||
|
func genResult0(w io.Writer, result string, alloc *int, top bool) string {
|
||||||
|
if result[0] != '(' {
|
||||||
|
// variable
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
s := split(result[1 : len(result)-1])
|
||||||
|
var v string
|
||||||
|
var needsType bool
|
||||||
|
if top {
|
||||||
|
v = "v"
|
||||||
|
fmt.Fprintf(w, "v.Op = Op%s\n", s[0])
|
||||||
|
fmt.Fprintf(w, "v.Aux = nil\n")
|
||||||
|
fmt.Fprintf(w, "v.Args = v.argstorage[:0]\n")
|
||||||
|
} else {
|
||||||
|
v = fmt.Sprintf("v%d", *alloc)
|
||||||
|
*alloc++
|
||||||
|
fmt.Fprintf(w, "%s := v.Block.NewValue(Op%s, TypeInvalid, nil)\n", v, s[0])
|
||||||
|
needsType = true
|
||||||
|
}
|
||||||
|
for _, a := range s[1:] {
|
||||||
|
if a[0] == '<' {
|
||||||
|
// type restriction
|
||||||
|
t := a[1 : len(a)-1]
|
||||||
|
if t[0] == '{' {
|
||||||
|
t = t[1 : len(t)-1]
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s.Type = %s\n", v, t)
|
||||||
|
needsType = false
|
||||||
|
} else if a[0] == '[' {
|
||||||
|
// aux restriction
|
||||||
|
x := a[1 : len(a)-1]
|
||||||
|
if x[0] == '{' {
|
||||||
|
x = x[1 : len(x)-1]
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
|
||||||
|
} else if a[0] == '{' {
|
||||||
|
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, a[1:len(a)-1])
|
||||||
|
} else {
|
||||||
|
// regular argument (sexpr or variable)
|
||||||
|
x := genResult0(w, a, alloc, false)
|
||||||
|
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needsType {
|
||||||
|
fmt.Fprintf(w, "%s.SetType()\n", v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func split(s string) []string {
|
||||||
|
var r []string
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for s != "" {
|
||||||
|
d := 0 // depth of ({[<
|
||||||
|
nonsp := false // found a non-space char so far
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '(', '{', '[', '<':
|
||||||
|
d++
|
||||||
|
case ')', '}', ']', '>':
|
||||||
|
d--
|
||||||
|
case ' ', '\t':
|
||||||
|
if d == 0 && nonsp {
|
||||||
|
r = append(r, strings.TrimSpace(s[:i]))
|
||||||
|
s = s[i:]
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
nonsp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d != 0 {
|
||||||
|
panic("imbalanced expression: " + s)
|
||||||
|
}
|
||||||
|
if nonsp {
|
||||||
|
r = append(r, strings.TrimSpace(s))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
69
src/cmd/internal/ssa/schedule.go
Normal file
69
src/cmd/internal/ssa/schedule.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
// Schedule the Values in each Block. After this phase returns, the
|
||||||
|
// order of b.Values matters and is the order in which those values
|
||||||
|
// will appear in the assembly output. For now it generates an
|
||||||
|
// arbitrary valid schedule using a topological sort. TODO(khr):
|
||||||
|
// schedule smarter.
|
||||||
|
func schedule(f *Func) {
|
||||||
|
const (
|
||||||
|
unmarked = 0
|
||||||
|
found = 1
|
||||||
|
expanded = 2
|
||||||
|
done = 3
|
||||||
|
)
|
||||||
|
state := make([]byte, f.NumValues())
|
||||||
|
var queue []*Value //stack-like worklist. Contains found and expanded nodes.
|
||||||
|
var order []*Value
|
||||||
|
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
// Topologically sort the values in b.
|
||||||
|
order = order[:0]
|
||||||
|
for _, v := range b.Values {
|
||||||
|
if v.Op == OpPhi {
|
||||||
|
// Phis all go first. We handle phis specially
|
||||||
|
// because they may have self edges "a = phi(a, b, c)"
|
||||||
|
order = append(order, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if state[v.ID] != unmarked {
|
||||||
|
if state[v.ID] != done {
|
||||||
|
panic("bad state")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state[v.ID] = found
|
||||||
|
queue = append(queue, v)
|
||||||
|
for len(queue) > 0 {
|
||||||
|
v = queue[len(queue)-1]
|
||||||
|
switch state[v.ID] {
|
||||||
|
case found:
|
||||||
|
state[v.ID] = expanded
|
||||||
|
// Note that v is not popped. We leave it in place
|
||||||
|
// until all its children have been explored.
|
||||||
|
for _, w := range v.Args {
|
||||||
|
if w.Block == b && w.Op != OpPhi && state[w.ID] == unmarked {
|
||||||
|
state[w.ID] = found
|
||||||
|
queue = append(queue, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case expanded:
|
||||||
|
queue = queue[:len(queue)-1]
|
||||||
|
state[v.ID] = done
|
||||||
|
order = append(order, v)
|
||||||
|
default:
|
||||||
|
panic("bad state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy(b.Values, order)
|
||||||
|
}
|
||||||
|
// TODO: only allow one live mem type and one live flags type (x86)
|
||||||
|
// This restriction will force any loads (and any flag uses) to appear
|
||||||
|
// before the next store (flag update). This "anti-dependence" is not
|
||||||
|
// recorded explicitly in ssa form.
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
(TYPE T127bd68 int)
|
(TYPE T127bd68 int)
|
||||||
(TYPE T127bd68 int)
|
(TYPE T127bd68 int)
|
||||||
(DCL n T127bd68)
|
(DCL n T127bd68)
|
||||||
|
(AS n (LOAD (FP T127bd68 0)))
|
||||||
(DCL ~r1 T127bd68)
|
(DCL ~r1 T127bd68)
|
||||||
(DCL n T127bd68)
|
(DCL n T127bd68)
|
||||||
(DCL autotmp_0000 T127bd68)
|
(DCL autotmp_0000 T127bd68)
|
||||||
@ -29,7 +30,7 @@
|
|||||||
(IF (LT n (CINT 2)) .then0 .else0)
|
(IF (LT n (CINT 2)) .then0 .else0)
|
||||||
(LABEL .then0)
|
(LABEL .then0)
|
||||||
(AS ~r1 n)
|
(AS ~r1 n)
|
||||||
(AS (SP T127bd68 8) ~r1)
|
(AS (FP T127bd68 8) ~r1)
|
||||||
(RETURN)
|
(RETURN)
|
||||||
(GOTO .end0)
|
(GOTO .end0)
|
||||||
(LABEL .else0)
|
(LABEL .else0)
|
||||||
@ -42,5 +43,5 @@
|
|||||||
(CALL fib)
|
(CALL fib)
|
||||||
(AS autotmp_0001 (LOAD (SP T127bd68 8)))
|
(AS autotmp_0001 (LOAD (SP T127bd68 8)))
|
||||||
(AS ~r1 (ADD autotmp_0000 autotmp_0001))
|
(AS ~r1 (ADD autotmp_0000 autotmp_0001))
|
||||||
(AS (SP T127bd68 8) ~r1)
|
(AS (FP T127bd68 8) ~r1)
|
||||||
(RETURN)
|
(RETURN)
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
(DCL autotmp_0003 Tf5dd68)
|
(DCL autotmp_0003 Tf5dd68)
|
||||||
(DCL ~r1 Tf5dd68)
|
(DCL ~r1 Tf5dd68)
|
||||||
(DCL a Tf5dd68)
|
(DCL a Tf5dd68)
|
||||||
(AS n (LOAD (SP Tf5dd68 0)))
|
(AS n (LOAD (FP Tf5dd68 0)))
|
||||||
(AS a (CINT 0))
|
(AS a (CINT 0))
|
||||||
(AS b (CINT 1))
|
(AS b (CINT 1))
|
||||||
(AS i (CINT 0))
|
(AS i (CINT 0))
|
||||||
@ -58,5 +58,5 @@
|
|||||||
(AS i (ADD autotmp_0002 (CINT 1)))
|
(AS i (ADD autotmp_0002 (CINT 1)))
|
||||||
(GOTO .top0)
|
(GOTO .top0)
|
||||||
(LABEL .end0)
|
(LABEL .end0)
|
||||||
(AS (SP Tf5dd68 8) a)
|
(AS (FP Tf5dd68 8) a)
|
||||||
(RETURN)
|
(RETURN)
|
||||||
|
Loading…
Reference in New Issue
Block a user