-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathanalysis.jl
132 lines (108 loc) · 2.97 KB
/
analysis.jl
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
struct Call{F,A}
f::F
a::A
Call{F,A}(f,a...) where {F,A} = new(f, a)
end
Call(f, a...) = Call{typeof(f),typeof(a)}(f, a...)
argtypes(c::Call) = Base.typesof(c.a...)
types(c::Call) = (typeof(c.f), argtypes(c).parameters...)
method(c::Call) = which(c.f, argtypes(c))
method_expr(f, Ts::Type{<:Tuple}) =
:($f($([:(::$T) for T in Ts.parameters]...)))
method_expr(c::Call) = method_expr(f, argtypes(c))
function loc(c::Call)
meth = method(c)
"$(meth.file):$(meth.line)"
end
function code(c::Call; optimize = false)
codeinfo = code_typed(c.f, argtypes(c), optimize = optimize)
@assert length(codeinfo) == 1
codeinfo = codeinfo[1]
linearize!(codeinfo[1])
return codeinfo
end
function eachline(f, code, line = -1)
for l in code.code
if l isa LineNumberNode
line = l.line
else
f(line, l)
end
end
end
struct Warning
f
a::Type{<:Tuple}
line::Int
message::String
end
Warning(c::Call, line, message) = Warning(c.f, argtypes(c), line, message)
Warning(meth, message) = Warning(meth, -1, message)
# local variables
exprtype(code, x) = typeof(x)
exprtype(code, x::Expr) = x.typ
exprtype(code, x::QuoteNode) = typeof(x.value)
exprtype(code, x::SSAValue) = code.ssavaluetypes[x.id+1]
function assignments(code, l = -1)
assigns = Dict()
eachline(code, l) do line, ex
(isexpr(ex, :(=)) && isexpr(ex.args[1], SlotNumber)) || return
typ = exprtype(code, ex.args[2])
push!(get!(assigns, ex.args[1], []), (line, typ))
end
return assigns
end
function locals(warn, call)
l = method(call).line
c = code(call)[1]
as = assignments(c, l)
for (x, as) in as
length(unique(map(x->x[2],as))) == 1 && continue
var = c.slotnames[x.id]
for (l, t) in as
warn(call, l, "$var is assigned as $t")
end
end
end
# global variables
function globals(warn, call)
c = code(call)[1]
eachline(c) do line, ex
(isexpr(ex, :(=)) && isexpr(ex.args[2], GlobalRef)) || return
ref = ex.args[2]
isconst(ref.mod, ref.name) ||
warn(call, line, "uses global variable $(ref.mod).$(ref.name)")
end
end
# dynamic dispatch
rebuild(code, x) = x
rebuild(code, x::Expr) = Expr(x.head, rebuild.(code, x.args)...)
rebuild(code, x::SlotNumber) = code.slotnames[x.id]
function rebuild(code, x::SSAValue)
for ex in code.code
isexpr(ex, :(=)) && ex.args[1] == x && return rebuild(code, ex.args[2])
end
error("$x not found")
end
function dispatch(warn, call)
c = code(call, optimize = true)[1]
eachline(c, method(call).line) do line, ex
(isexpr(ex, :(=)) && isexpr(ex.args[2], :call)) || return
callex = rebuild(c, ex.args[2])
f = callex.args[1]
f isa GlobalRef && isprimitive(getfield(f.mod, f.name)) && return
warn(call, line, "dynamic dispatch to $(callex)")
end
end
# return type
function rettype(warn, call)
c, out = code(call)
isleaftype(out) || warn(call, "returns $out")
end
# overall analysis
function analyse(warn, call)
globals(warn, call)
locals(warn, call)
dispatch(warn, call)
rettype(warn, call)
end