Skip to content

Commit 0229863

Browse files
author
Amit Davidson
committed
Change cfg to be more efficient by not calculating all paths
1 parent b457a41 commit 0229863

File tree

5 files changed

+46
-157
lines changed

5 files changed

+46
-157
lines changed

Diff for: domain/BlockState.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ func (existingBlock *BlockState) AddResult(newBlock *BlockState, shouldMergeLock
3434
// Merge child B unto A:
3535
// A -> B
3636
// Will Merge B unto A
37-
func (existingBlock *BlockState) MergeChildBlock(newBlock *BlockState, shouldUpdateGALockset bool) {
38-
if shouldUpdateGALockset {
39-
for _, guardedAccess := range newBlock.GuardedAccesses {
40-
guardedAccess.Lockset.UpdateLockSet(existingBlock.Lockset.Locks, existingBlock.Lockset.Unlocks)
41-
}
37+
func (existingBlock *BlockState) MergeChildBlock(newBlock *BlockState) {
38+
for _, guardedAccess := range newBlock.GuardedAccesses {
39+
guardedAccess.Lockset.UpdateLockSet(existingBlock.Lockset.Locks, existingBlock.Lockset.Unlocks)
4240
}
4341
existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newBlock.GuardedAccesses...)
4442
existingBlock.DeferredFunctions.MergeStacks(newBlock.DeferredFunctions)

Diff for: ssaUtils/CFG.go

+25-55
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,51 @@ import (
77
)
88

99
type CFG struct {
10-
visitedBlocksStack *stacks.BasicBlockStack
10+
visitedBlocksStack *stacks.BlockMap
1111

1212
ComputedBlocks map[int]*domain.BlockState
1313
ComputedDeferBlocks map[int]*domain.BlockState
14-
calculatedState *domain.BlockState
1514
}
1615

1716
func newCFG() *CFG {
1817
return &CFG{
19-
visitedBlocksStack: stacks.NewBasicBlockStack(),
18+
visitedBlocksStack: stacks.NewBlockMap(),
2019
ComputedBlocks: make(map[int]*domain.BlockState),
2120
ComputedDeferBlocks: make(map[int]*domain.BlockState),
2221
}
2322
}
2423

25-
func (cfg *CFG) calculateFunctionStatePathSensitive(context *domain.Context, block *ssa.BasicBlock) {
26-
firstBlock := &ssa.BasicBlock{Index: -1, Succs: []*ssa.BasicBlock{block}}
27-
cfg.traverseGraph(context, firstBlock)
28-
}
24+
func (cfg *CFG) CalculateFunctionStatePathSensitive(context *domain.Context, block *ssa.BasicBlock) *domain.BlockState {
25+
cfg.visitedBlocksStack.Add(block)
26+
defer cfg.visitedBlocksStack.Remove(block)
27+
cfg.calculateBlockState(context, block)
28+
29+
// Regular flow
30+
blockState := cfg.ComputedBlocks[block.Index]
2931

30-
func (cfg *CFG) traverseGraph(context *domain.Context, block *ssa.BasicBlock) {
32+
// recursion
33+
var branchState *domain.BlockState
3134
for _, nextBlock := range block.Succs {
32-
cfg.calculateBlockState(context, block)
33-
if len(nextBlock.Succs) == 0 || cfg.visitedBlocksStack.Contains(nextBlock) {
34-
// If a return is reached or if a cycle of a loop is completed.
35-
cfg.calculateBlockState(context, nextBlock)
36-
cfg.visitedBlocksStack.Push(nextBlock)
37-
cfg.CalculatePath()
38-
cfg.visitedBlocksStack.Pop()
39-
} else {
40-
cfg.visitedBlocksStack.Push(nextBlock)
41-
cfg.traverseGraph(context, nextBlock)
42-
cfg.visitedBlocksStack.Pop()
35+
// if it's a cycle we skip it
36+
if cfg.visitedBlocksStack.Contains(nextBlock.Index) {
37+
continue
4338
}
44-
}
45-
}
46-
47-
func (cfg *CFG) CalculatePath() {
48-
path := cfg.visitedBlocksStack.GetItems()
49-
block := path[0]
50-
state := cfg.ComputedBlocks[block.Index].Copy()
51-
for _, nextBlock := range path[1:] {
52-
nextState := cfg.ComputedBlocks[nextBlock.Index].Copy()
53-
state.MergeChildBlock(nextState, true)
54-
}
55-
56-
var firstDeferState *domain.BlockState
57-
var firstDeferIndex int
58-
for i := len(path) - 1; i >= 0; i-- {
59-
deferIndex := path[i].Index
60-
deferState, ok := cfg.ComputedDeferBlocks[deferIndex]
61-
if ok {
62-
firstDeferState = deferState
63-
firstDeferIndex = i
64-
break
39+
retBlockState := cfg.CalculateFunctionStatePathSensitive(context, nextBlock)
40+
if branchState == nil {
41+
branchState = retBlockState.Copy()
42+
} else {
43+
branchState.MergeSiblingBlock(retBlockState)
6544
}
6645
}
67-
if firstDeferState != nil {
68-
deferStateCopy := firstDeferState.Copy()
69-
for i := firstDeferIndex - 1; i >= 0; i-- {
70-
nextState, ok := cfg.ComputedDeferBlocks[path[i].Index]
71-
if !ok {
72-
continue
73-
}
74-
nextStateCopy := nextState.Copy()
75-
deferStateCopy.MergeChildBlock(nextStateCopy, true)
76-
}
77-
state.AddResult(deferStateCopy, true)
46+
if branchState != nil {
47+
blockState.MergeChildBlock(branchState)
7848
}
7949

80-
if cfg.calculatedState == nil {
81-
cfg.calculatedState = state
82-
} else {
83-
cfg.calculatedState.MergeSiblingBlock(state)
50+
// Defer
51+
if deferState, ok := cfg.ComputedDeferBlocks[block.Index]; ok {
52+
blockState.MergeChildBlock(deferState)
8453
}
54+
return blockState
8555
}
8656

8757
func (cfg *CFG) calculateBlockState(context *domain.Context, block *ssa.BasicBlock) {

Diff for: ssaUtils/Functions.go

+6-28
Original file line numberDiff line numberDiff line change
@@ -171,35 +171,19 @@ func GetBlockSummary(context *domain.Context, block *ssa.BasicBlock) *domain.Blo
171171

172172
func (cfg *CFG) runDefers(context *domain.Context, defers *stacks.CallCommonStack) *domain.BlockState {
173173
calculatedState := domain.GetEmptyBlockState()
174-
for {
175-
deferFunction := defers.Pop()
174+
defersItems := defers.GetItems()
175+
for i := len(defersItems) - 1; i >= 0; i-- {
176+
deferFunction := defersItems[i]
176177
if deferFunction == nil {
177178
break
178179
}
179180
retState := HandleCallCommon(context, deferFunction, deferFunction.Pos())
180-
calculatedState.MergeChildBlock(retState, true)
181+
calculatedState.MergeChildBlock(retState)
181182
}
182183
return calculatedState
183184

184185
}
185186

186-
func (cfg *CFG) calculateFunctionStatePathInsensitive(context *domain.Context, blocks []*ssa.BasicBlock) {
187-
for _, block := range blocks {
188-
cfg.calculateBlockState(context, block)
189-
state := cfg.ComputedBlocks[block.Index]
190-
if cfg.calculatedState == nil {
191-
cfg.calculatedState = state.Copy()
192-
} else {
193-
cfg.calculatedState.MergeChildBlock(state, false)
194-
}
195-
196-
deferBlock := cfg.ComputedDeferBlocks[block.Index]
197-
if deferBlock != nil {
198-
cfg.calculatedState.MergeChildBlock(deferBlock, false)
199-
}
200-
}
201-
}
202-
203187
func HandleFunction(context *domain.Context, fn *ssa.Function) *domain.BlockState {
204188
funcState := domain.GetEmptyBlockState()
205189
if fn.Pkg == nil {
@@ -219,12 +203,6 @@ func HandleFunction(context *domain.Context, fn *ssa.Function) *domain.BlockStat
219203
panic("Function is being iterated but wasn't found when iterating on program functions in preprocess")
220204
}
221205
cfg := newCFG()
222-
cfg.calculateFunctionStatePathSensitive(context, fn.Blocks[0])
223-
//if isContainingLocks {
224-
// cfg.calculateFunctionStatePathSensitive(context, fn.Blocks[0])
225-
//} else {
226-
// cfg.calculateFunctionStatePathInsensitive(context, fn.Blocks)
227-
//}
228-
229-
return cfg.calculatedState
206+
calculatedState := cfg.CalculateFunctionStatePathSensitive(context, fn.Blocks[0])
207+
return calculatedState
230208
}

Diff for: utils/stacks/basicBlockStack.go

+11-66
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,23 @@ import "golang.org/x/tools/go/ssa"
44

55
// Used by CFG to traverse the graph. It uses both as a stack for traversal by order, and as a map to count occurrences
66
// and fast retrieval of items.
7-
type BasicBlockStack struct {
8-
stack blocksStack
9-
blocksMap blocksMap
10-
}
11-
12-
func NewBasicBlockStack() *BasicBlockStack {
13-
stack := make([]*ssa.BasicBlock, 0)
14-
blocksMap := make(blocksMap)
15-
basicBlockStack := &BasicBlockStack{stack: stack, blocksMap: blocksMap}
16-
return basicBlockStack
17-
}
187

19-
func (s *BasicBlockStack) Push(v *ssa.BasicBlock) {
20-
s.stack.Push(v)
21-
if _, ok := s.blocksMap[v.Index]; !ok {
22-
s.blocksMap[v.Index] = &basicBlockWithCount{block: v, count: 1}
23-
} else {
24-
s.blocksMap[v.Index].count += 1
25-
}
26-
}
8+
type BlockMap map[int]struct{}
279

28-
func (s *BasicBlockStack) Pop() *ssa.BasicBlock {
29-
v := s.stack.Pop()
30-
if v == nil {
31-
return nil
32-
}
33-
s.blocksMap[v.Index].count -= 1
34-
if s.blocksMap[v.Index].count == 0 {
35-
delete(s.blocksMap, v.Index)
36-
}
37-
return v
10+
func NewBlockMap() *BlockMap {
11+
blocksMap := make(BlockMap)
12+
return &blocksMap
3813
}
3914

40-
func (s *BasicBlockStack) GetItems() []*ssa.BasicBlock {
41-
return s.stack
15+
func (s *BlockMap) Add(v *ssa.BasicBlock) {
16+
(*s)[v.Index] = struct{}{}
4217
}
4318

44-
type basicBlockWithCount struct {
45-
block *ssa.BasicBlock
46-
count int
19+
func (s *BlockMap) Remove(v *ssa.BasicBlock) {
20+
delete(*s, v.Index)
4721
}
4822

49-
type blocksMap map[int]*basicBlockWithCount
50-
51-
type blocksStack []*ssa.BasicBlock
52-
53-
func (s *blocksStack) Push(v *ssa.BasicBlock) {
54-
*s = append(*s, v)
55-
}
56-
57-
func (s *blocksStack) Pop() *ssa.BasicBlock {
58-
if len(*s) == 0 {
59-
return nil
60-
}
61-
v := (*s)[len(*s)-1]
62-
*s = (*s)[:len(*s)-1]
63-
return v
64-
}
65-
66-
67-
// The function check if the block already exists in the stack and if it does, return true only if it appear twice or more.
68-
// This is used to handle cycles where a path might visit a node more then once. This way a cycle is allowed, but only once
69-
// to avoid infinite recursion. Finding all valid flows is hard so this is chosen as an approximation. There are edge
70-
//cases such as: A->B->D->C->D->E(end) where the graph is not circular, but the traversal won't reach to E since D
71-
//appear a second time and it will exit at that point. This flow is rare and can be achieved using gotos
72-
func (s *BasicBlockStack) Contains(v *ssa.BasicBlock) bool {
73-
block, ok := s.blocksMap[v.Index]
74-
if !ok {
75-
return false
76-
}
77-
if block.count >= 1 {
78-
return true
79-
}
80-
return false
23+
func (s *BlockMap) Contains(v int) bool {
24+
_, ok := (*s)[v]
25+
return ok
8126
}

Diff for: utils/stacks/callCommonStack.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ func NewCallCommonStack() *CallCommonStack {
1010
}
1111

1212
func (s *CallCommonStack) GetItems() []*ssa.CallCommon {
13-
tmp := make([]*ssa.CallCommon, len(*s))
14-
copy(tmp, *s)
15-
return tmp
13+
return *s
1614
}
1715

1816
func (s *CallCommonStack) Push(v *ssa.CallCommon) {

0 commit comments

Comments
 (0)