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.
@@ -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