# # # The Nim Compiler # (c) Copyright 2020 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## Optimizer: ## - elide 'wasMoved(x); destroy(x)' pairs ## - recognize "all paths lead to 'wasMoved(x)'" import ast, renderer, idents from trees import exprStructuralEquivalent import std/[strutils, intsets] const nfMarkForDeletion = nfNone # faster than a lookup table type BasicBlock = object wasMovedLocs: seq[PNode] kind: TNodeKind hasReturn, hasBreak: bool label: PSym # can be nil parent: ptr BasicBlock Con = object somethingTodo: bool inFinally: int proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock = BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false, label: nil, parent: addr(parent)) proc breakStmt(b: var BasicBlock; n: PNode) = var it = addr(b) while it != nil: it.wasMovedLocs.setLen 0 it.hasBreak = true if n.kind == nkSym: if it.label == n.sym: break else: # unnamed break leaves the block is nkWhileStmt or the like: if it.kind in {nkWhileStmt, nkBlockStmt, nkBlockExpr}: break it = it.parent proc returnStmt(b: var BasicBlock) = b.hasReturn = true var it = addr(b) while it != nil: it.wasMovedLocs.setLen 0 it = it.parent proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} = if this.hasReturn: parent.wasMovedLocs.setLen 0 parent.hasReturn = true proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool = result = false for i in 0.. 0 and (b.hasReturn or b.hasBreak): discard "cannot optimize away the destructor" else: c.wasMovedDestroyPair b, n special = true elif name == "=sink": reverse = true if not special: if not reverse: for i in 0 ..< n.len: analyse(c, b, n[i]) else: #[ Test destructor/tmatrix.test3: Prevent this from being elided. We should probably find a better solution... `=sink`(b, - let blitTmp = b; wasMoved(b); blitTmp + a) `=destroy`(b) ]# for i in countdown(n.len-1, 0): analyse(c, b, n[i]) if canRaise(n[0]): returnStmt(b) of nkSym: # any usage of the location before destruction implies we # cannot elide the 'wasMoved(x)': b.invalidateWasMoved n of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr, nkMixinStmt, nkBindStmt: discard "do not follow the construct" of nkAsgn, nkFastAsgn, nkSinkAsgn: # reverse order, see remark for `=sink`: analyse(c, b, n[1]) analyse(c, b, n[0]) of nkIfStmt, nkIfExpr: let isExhaustive = n[^1].kind in {nkElse, nkElseExpr} var wasMovedSet: seq[PNode] = @[] for i in 0 ..< n.len: var branch = nestedBlock(b, n[i].kind) analyse(c, branch, n[i]) mergeBasicBlockInfo(b, branch) if isExhaustive: if i == 0: wasMovedSet = move(branch.wasMovedLocs) else: wasMovedSet.intersect(branch.wasMovedLocs) for i in 0..