idlc 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/bin/idlc +10 -0
- data/lib/idlc/ast.rb +9611 -0
- data/lib/idlc/ast_decl.rb +22 -0
- data/lib/idlc/cli.rb +232 -0
- data/lib/idlc/idl.treetop +675 -0
- data/lib/idlc/idl_parser.rb +15386 -0
- data/lib/idlc/interfaces.rb +135 -0
- data/lib/idlc/log.rb +23 -0
- data/lib/idlc/passes/find_referenced_csrs.rb +39 -0
- data/lib/idlc/passes/find_return_values.rb +76 -0
- data/lib/idlc/passes/find_src_registers.rb +125 -0
- data/lib/idlc/passes/gen_adoc.rb +355 -0
- data/lib/idlc/passes/gen_option_adoc.rb +169 -0
- data/lib/idlc/passes/prune.rb +957 -0
- data/lib/idlc/passes/reachable_exceptions.rb +206 -0
- data/lib/idlc/passes/reachable_functions.rb +221 -0
- data/lib/idlc/symbol_table.rb +549 -0
- data/lib/idlc/syntax_node.rb +64 -0
- data/lib/idlc/type.rb +992 -0
- data/lib/idlc/version.rb +10 -0
- data/lib/idlc.rb +409 -0
- metadata +394 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
3
|
+
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
require_relative "prune"
|
|
7
|
+
|
|
8
|
+
# finds all reachable exceptions from a give sequence of statements
|
|
9
|
+
|
|
10
|
+
module Idl
|
|
11
|
+
class AstNode
|
|
12
|
+
# @return [Array<FunctionBodyAst>] List of all functions that can be reached (via function calls) from this node
|
|
13
|
+
def reachable_exceptions(symtab, cache = {})
|
|
14
|
+
return 0 if @children.empty?
|
|
15
|
+
|
|
16
|
+
mask = 0
|
|
17
|
+
@children.size.times do |i|
|
|
18
|
+
mask |= @children[i].reachable_exceptions(symtab, cache)
|
|
19
|
+
end
|
|
20
|
+
mask
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class FunctionCallExpressionAst
|
|
25
|
+
def reachable_exceptions(symtab, cache = {})
|
|
26
|
+
if name == "raise" || name == "raise_precise"
|
|
27
|
+
# first argument is the exception
|
|
28
|
+
code_ast = arg_nodes[0]
|
|
29
|
+
value_result = value_try do
|
|
30
|
+
code = code_ast.value(symtab)
|
|
31
|
+
internal_error "Code should be an integer" unless code.is_a?(Integer)
|
|
32
|
+
return 1 << code
|
|
33
|
+
end
|
|
34
|
+
value_else(value_result) do
|
|
35
|
+
value_error "Cannot determine value of exception code"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# return @reachable_exceptions_func_call_cache[symtab] unless @reachable_exceptions_func_call_cache[symtab].nil?
|
|
40
|
+
|
|
41
|
+
func_def_type = func_type(symtab)
|
|
42
|
+
|
|
43
|
+
mask = 0
|
|
44
|
+
arg_nodes.each do |a|
|
|
45
|
+
mask |= a.reachable_exceptions(symtab, cache) if a.is_a?(FunctionCallExpressionAst)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
unless func_def_type.builtin? || func_def_type.generated?
|
|
49
|
+
body_symtab = symtab.global_clone
|
|
50
|
+
body_symtab.push(func_def_type.func_def_ast)
|
|
51
|
+
avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self)
|
|
52
|
+
|
|
53
|
+
idx = [name, [], avals].hash
|
|
54
|
+
|
|
55
|
+
begin
|
|
56
|
+
body_mask =
|
|
57
|
+
if cache.key?(idx)
|
|
58
|
+
cache[idx]
|
|
59
|
+
else
|
|
60
|
+
cache[idx] = func_def_type.body.reachable_exceptions(body_symtab, cache)
|
|
61
|
+
end
|
|
62
|
+
mask |= body_mask
|
|
63
|
+
ensure
|
|
64
|
+
body_symtab.pop
|
|
65
|
+
body_symtab.release
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @reachable_exceptions_func_call_cache[symtab] = mask
|
|
70
|
+
mask
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class StatementAst
|
|
75
|
+
def reachable_exceptions(symtab, cache = {})
|
|
76
|
+
mask =
|
|
77
|
+
# if action.is_a?(FunctionCallExpressionAst)
|
|
78
|
+
action.reachable_exceptions(symtab, cache)
|
|
79
|
+
# else
|
|
80
|
+
# 0
|
|
81
|
+
# end
|
|
82
|
+
action.add_symbol(symtab) if action.is_a?(Declaration)
|
|
83
|
+
if action.is_a?(Executable)
|
|
84
|
+
value_try do
|
|
85
|
+
action.execute(symtab)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
# ok
|
|
89
|
+
mask
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class IfAst
|
|
94
|
+
def reachable_exceptions(symtab, cache = {})
|
|
95
|
+
mask = 0
|
|
96
|
+
value_try do
|
|
97
|
+
mask = if_cond.reachable_exceptions(symtab, cache) if if_cond.is_a?(FunctionCallExpressionAst)
|
|
98
|
+
value_result = value_try do
|
|
99
|
+
if (if_cond.value(symtab))
|
|
100
|
+
mask |= if_body.reachable_exceptions(symtab, cache)
|
|
101
|
+
return mask # no need to continue
|
|
102
|
+
else
|
|
103
|
+
elseifs.each do |eif|
|
|
104
|
+
mask |= eif.cond.reachable_exceptions(symtab, cache) if eif.cond.is_a?(FunctionCallExpressionAst)
|
|
105
|
+
value_result = value_try do
|
|
106
|
+
if (eif.cond.value(symtab))
|
|
107
|
+
mask |= eif.body.reachable_exceptions(symtab, cache)
|
|
108
|
+
return mask # no need to keep going
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
value_else(value_result) do
|
|
112
|
+
# condition isn't known; body is potentially reachable
|
|
113
|
+
mask |= eif.body.reachable_exceptions(symtab, cache)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
mask |= final_else_body.reachable_exceptions(symtab, cache)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
value_else(value_result) do
|
|
120
|
+
mask |= if_body.reachable_exceptions(symtab, cache)
|
|
121
|
+
|
|
122
|
+
elseifs.each do |eif|
|
|
123
|
+
mask |= eif.cond.reachable_exceptions(symtab, cache) if eif.cond.is_a?(FunctionCallExpressionAst)
|
|
124
|
+
value_result = value_try do
|
|
125
|
+
if (eif.cond.value(symtab))
|
|
126
|
+
mask |= eif.body.reachable_exceptions(symtab, cache)
|
|
127
|
+
return mask # no need to keep going
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
value_else(value_result) do
|
|
131
|
+
# condition isn't known; body is potentially reachable
|
|
132
|
+
mask |= eif.body.reachable_exceptions(symtab, cache)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
mask |= final_else_body.reachable_exceptions(symtab, cache)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
return mask
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class ConditionalReturnStatementAst
|
|
143
|
+
def reachable_exceptions(symtab, cache = {})
|
|
144
|
+
mask = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_exceptions(symtab, cache) : 0
|
|
145
|
+
value_result = value_try do
|
|
146
|
+
if condition.value(symtab)
|
|
147
|
+
mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab, cache) : 0
|
|
148
|
+
# ok
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
value_else(value_result) do
|
|
152
|
+
mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab, cache) : 0
|
|
153
|
+
end
|
|
154
|
+
mask
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class ConditionalStatementAst
|
|
159
|
+
def reachable_exceptions(symtab, cache = {})
|
|
160
|
+
mask = 0
|
|
161
|
+
value_result = value_try do
|
|
162
|
+
mask |= condition.reachable_exceptions(symtab, cache)
|
|
163
|
+
if condition.value(symtab)
|
|
164
|
+
mask |= action.reachable_exceptions(symtab, cache)
|
|
165
|
+
action.add_symbol(symtab) if action.is_a?(Declaration)
|
|
166
|
+
if action.is_a?(Executable)
|
|
167
|
+
value_result = value_try do
|
|
168
|
+
action.execute(symtab)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
value_else(value_result) do
|
|
174
|
+
mask = 0
|
|
175
|
+
# condition not known
|
|
176
|
+
mask |= condition.reachable_exceptions(symtab, cache)
|
|
177
|
+
mask |= action.reachable_exceptions(symtab, cache)
|
|
178
|
+
action.add_symbol(symtab) if action.is_a?(Declaration)
|
|
179
|
+
if action.is_a?(Executable)
|
|
180
|
+
value_result = value_try do
|
|
181
|
+
action.execute(symtab)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
mask
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
class ForLoopAst
|
|
190
|
+
def reachable_exceptions(symtab, cache = {})
|
|
191
|
+
symtab.push(self)
|
|
192
|
+
begin
|
|
193
|
+
symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab)))
|
|
194
|
+
mask = init.is_a?(FunctionCallExpressionAst) ? init.reachable_exceptions(symtab, cache) : 0
|
|
195
|
+
mask |= condition.reachable_exceptions(symtab, cache) if condition.is_a?(FunctionCallExpressionAst)
|
|
196
|
+
mask |= update.reachable_exceptions(symtab, cache) if update.is_a?(FunctionCallExpressionAst)
|
|
197
|
+
stmts.each do |stmt|
|
|
198
|
+
mask |= stmt.reachable_exceptions(symtab, cache)
|
|
199
|
+
end
|
|
200
|
+
ensure
|
|
201
|
+
symtab.pop
|
|
202
|
+
end
|
|
203
|
+
mask
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# typed: false
|
|
2
|
+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
4
|
+
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
# finds all reachable functions from a give sequence of statements
|
|
8
|
+
|
|
9
|
+
module Idl
|
|
10
|
+
class AstNode
|
|
11
|
+
ReachableFunctionCacheType = T.type_alias { T::Hash[T::Array[T.untyped], T::Array[FunctionDefAst]] }
|
|
12
|
+
|
|
13
|
+
# @return [Array<FunctionDefAst>] List of all functions that can be reached (via function calls) from this node
|
|
14
|
+
sig {
|
|
15
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
16
|
+
.returns(T::Array[FunctionDefAst])
|
|
17
|
+
}
|
|
18
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
19
|
+
seen = {}
|
|
20
|
+
children.each_with_object([]) do |child, result|
|
|
21
|
+
child.reachable_functions(symtab, cache).each do |fn|
|
|
22
|
+
unless seen.key?(fn.name)
|
|
23
|
+
seen[fn.name] = true
|
|
24
|
+
result << fn
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class FunctionCallExpressionAst
|
|
32
|
+
sig {
|
|
33
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
34
|
+
.returns(T::Array[FunctionDefAst])
|
|
35
|
+
}
|
|
36
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
37
|
+
func_def_type = func_type(symtab)
|
|
38
|
+
|
|
39
|
+
body_symtab = symtab.global_clone
|
|
40
|
+
body_symtab.push(func_def_type.func_def_ast)
|
|
41
|
+
|
|
42
|
+
# Use a hash keyed by name to accumulate unique functions without repeated uniq scans
|
|
43
|
+
fns_by_name = {}
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
arg_nodes.each do |a|
|
|
47
|
+
a.reachable_functions(symtab, cache).each { |fn| fns_by_name[fn.name] ||= fn }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
unless func_def_type.builtin? || func_def_type.generated?
|
|
51
|
+
avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self)
|
|
52
|
+
|
|
53
|
+
idx = [name, avals].hash
|
|
54
|
+
|
|
55
|
+
if cache.key?(idx)
|
|
56
|
+
# Use cached results from a prior traversal (e.g., same function called
|
|
57
|
+
# by an earlier instruction). The sentinel [] handles recursion cycles.
|
|
58
|
+
cache[idx].each { |fn| fns_by_name[fn.name] ||= fn }
|
|
59
|
+
else
|
|
60
|
+
cache[idx] = [] # sentinel: breaks recursion cycles before body is traversed
|
|
61
|
+
body_fns = func_def_type.body.reachable_functions(body_symtab, cache)
|
|
62
|
+
cache[idx] = body_fns
|
|
63
|
+
body_fns.each { |fn| fns_by_name[fn.name] ||= fn }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
fns_by_name[func_def_type.func_def_ast.name] ||= func_def_type.func_def_ast
|
|
68
|
+
ensure
|
|
69
|
+
body_symtab.pop
|
|
70
|
+
body_symtab.release
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
fns_by_name.values
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class StatementAst
|
|
78
|
+
sig {
|
|
79
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
80
|
+
.returns(T::Array[FunctionDefAst])
|
|
81
|
+
}
|
|
82
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
83
|
+
fns = action.reachable_functions(symtab, cache)
|
|
84
|
+
|
|
85
|
+
action.add_symbol(symtab) if action.is_a?(Declaration)
|
|
86
|
+
value_try do
|
|
87
|
+
action.execute(symtab) if action.is_a?(Executable)
|
|
88
|
+
rescue SystemStackError
|
|
89
|
+
type_error "Detected unbounded recursion during compile-time constant evaluation at #{input_file}:#{input_line}.. This recursion cannot be represented or validated."
|
|
90
|
+
end
|
|
91
|
+
# ok
|
|
92
|
+
|
|
93
|
+
fns
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class IfAst
|
|
99
|
+
sig {
|
|
100
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
101
|
+
.returns(T::Array[FunctionDefAst])
|
|
102
|
+
}
|
|
103
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
104
|
+
fns = []
|
|
105
|
+
value_try do
|
|
106
|
+
fns.concat if_cond.reachable_functions(symtab, cache)
|
|
107
|
+
value_result = value_try do
|
|
108
|
+
if (if_cond.value(symtab))
|
|
109
|
+
fns.concat if_body.reachable_functions(symtab, cache)
|
|
110
|
+
return fns # no need to continue
|
|
111
|
+
else
|
|
112
|
+
if (if_cond.text_value == "pending_and_enabled_interrupts != 0")
|
|
113
|
+
warn symtab.get("pending_and_enabled_interrupts")
|
|
114
|
+
raise "???"
|
|
115
|
+
end
|
|
116
|
+
elseifs.each do |eif|
|
|
117
|
+
fns.concat eif.cond.reachable_functions(symtab, cache)
|
|
118
|
+
value_result = value_try do
|
|
119
|
+
if (eif.cond.value(symtab))
|
|
120
|
+
fns.concat eif.body.reachable_functions(symtab, cache)
|
|
121
|
+
return fns # no need to keep going
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
value_else(value_result) do
|
|
125
|
+
# condition isn't known; body is potentially reachable
|
|
126
|
+
fns.concat eif.body.reachable_functions(symtab, cache)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
fns.concat final_else_body.reachable_functions(symtab, cache)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
value_else(value_result) do
|
|
133
|
+
fns.concat if_body.reachable_functions(symtab, cache)
|
|
134
|
+
|
|
135
|
+
elseifs.each do |eif|
|
|
136
|
+
fns.concat eif.cond.reachable_functions(symtab, cache)
|
|
137
|
+
value_result = value_try do
|
|
138
|
+
if (eif.cond.value(symtab))
|
|
139
|
+
fns.concat eif.body.reachable_functions(symtab, cache)
|
|
140
|
+
return fns # no need to keep going
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
value_else(value_result) do
|
|
144
|
+
# condition isn't known; body is potentially reachable
|
|
145
|
+
fns.concat eif.body.reachable_functions(symtab, cache)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
fns.concat final_else_body.reachable_functions(symtab, cache)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
return fns
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
class ConditionalReturnStatementAst
|
|
156
|
+
sig {
|
|
157
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
158
|
+
.returns(T::Array[FunctionDefAst])
|
|
159
|
+
}
|
|
160
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
161
|
+
fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab, cache) : []
|
|
162
|
+
value_result = value_try do
|
|
163
|
+
cv = condition.value(symtab)
|
|
164
|
+
if cv
|
|
165
|
+
fns.concat return_expression.reachable_functions(symtab, cache)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
value_else(value_result) do
|
|
169
|
+
fns.concat return_expression.reachable_functions(symtab, cache)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
fns
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
class ConditionalStatementAst
|
|
177
|
+
sig {
|
|
178
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
179
|
+
.returns(T::Array[FunctionDefAst])
|
|
180
|
+
}
|
|
181
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
182
|
+
|
|
183
|
+
fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab, cache) : []
|
|
184
|
+
|
|
185
|
+
value_result = value_try do
|
|
186
|
+
if condition.value(symtab)
|
|
187
|
+
fns.concat action.reachable_functions(symtab, cache)
|
|
188
|
+
# no need to execute action (return)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
value_else(value_result) do
|
|
192
|
+
# condition not known
|
|
193
|
+
fns = fns.concat action.reachable_functions(symtab, cache)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
fns
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class ForLoopAst
|
|
201
|
+
sig {
|
|
202
|
+
params(symtab: SymbolTable, cache: ReachableFunctionCacheType )
|
|
203
|
+
.returns(T::Array[FunctionDefAst])
|
|
204
|
+
}
|
|
205
|
+
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
|
|
206
|
+
symtab.push(self)
|
|
207
|
+
begin
|
|
208
|
+
symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab)))
|
|
209
|
+
fns = init.is_a?(FunctionCallExpressionAst) ? init.reachable_functions(symtab, cache) : []
|
|
210
|
+
fns.concat(condition.reachable_functions(symtab, cache))
|
|
211
|
+
fns.concat(update.reachable_functions(symtab, cache))
|
|
212
|
+
stmts.each do |stmt|
|
|
213
|
+
fns.concat(stmt.reachable_functions(symtab, cache))
|
|
214
|
+
end
|
|
215
|
+
ensure
|
|
216
|
+
symtab.pop
|
|
217
|
+
end
|
|
218
|
+
fns
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|