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,957 @@
1
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: false
5
+ # frozen_string_literal: true
6
+
7
+ # This file contains AST functions that prune out unreachable paths given
8
+ # some known values in a symbol table
9
+ # It adds a `prune` function to every AstNode that returns a new,
10
+ # pruned subtree.
11
+
12
+ require "sorbet-runtime"
13
+
14
+ require_relative "../ast"
15
+
16
+ module Idl
17
+ module PruneHelpers
18
+ extend T::Sig
19
+ def self.create_int_literal(value, forced_type: nil)
20
+ width = forced_type ? forced_type.width : value.bit_length
21
+ raise "pruning error: attempting to prune an integer with unknown width" unless width.is_a?(Integer)
22
+ width = 1 if width == 0
23
+ v = value <= 512 ? value.to_s : "h#{value.to_s(16)}"
24
+ str = "#{width}'#{v}"
25
+ Idl::IntLiteralAst.new(str, 0...str.size, str)
26
+ end
27
+
28
+ def self.create_bool_literal(value)
29
+ if value
30
+ Idl::TrueExpressionAst.new("true", 0..4)
31
+ else
32
+ Idl::FalseExpressionAst.new("false", 0..5)
33
+ end
34
+ end
35
+
36
+ # returns nil if array holds bools
37
+ # otherwise (it holds bits), returns max bitwidth of all elements
38
+ sig { params(symtab: Idl::SymbolTable, node: Idl::AstNode, max: T.nilable(Integer)).returns(T.nilable(Integer)) }
39
+ def self.find_max_element_width(symtab, node, max = nil)
40
+ if node.is_a?(Idl::ArrayLiteralAst)
41
+ node.entries.map do |e|
42
+ e_max = find_max_element_width(symtab, e)
43
+ max.nil? ? e_max : [max, e_max].max
44
+ end.max
45
+ else
46
+ if node.is_a?(Idl::TrueExpressionAst) || node.is_a?(Idl::FalseExpressionAst)
47
+ nil
48
+ else
49
+ node_width = node.type(symtab).width
50
+ max.nil? ? node_width : [max, node_width].max
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.coerce_ary_element_widths(symtab, elements, max_element_width)
56
+ if elements.is_a?(Array) && elements.empty?
57
+ Idl::ArrayLiteralAst.new("pruned_literal_ary", 0..18, [])
58
+ elsif elements.fetch(0).is_a?(Idl::ArrayLiteralAst)
59
+ # Recursively coerce nested arrays - pass e.entries, not e
60
+ Idl::ArrayLiteralAst.new("pruned_literal_ary", 0..18,
61
+ elements.map { |e| coerce_ary_element_widths(symtab, e.entries, max_element_width) })
62
+ else
63
+ # Base case: elements is an array of leaf nodes, coerce each to max_element_width
64
+ coerced = elements.map { |node| create_int_literal(node.value(symtab), forced_type: Idl::Type.new(:bits, width: max_element_width)) }
65
+ Idl::ArrayLiteralAst.new("pruned_literal_ary", 0..18, coerced)
66
+ end
67
+ end
68
+
69
+ def self.create_literal(symtab, value, type, forced_type: nil)
70
+ case type.kind
71
+ when :enum_ref
72
+ member_name = type.enum_class.element_names[type.enum_class.element_values.index(value)]
73
+ str = "#{type.enum_class.name}::#{member_name}"
74
+ Idl::EnumRefAst.new(str, 0...str.size, type.enum_class.name, member_name)
75
+ when :bits
76
+ create_int_literal(value, forced_type:)
77
+ when :boolean
78
+ create_bool_literal(value)
79
+ when :array
80
+ elements = value.map { |e| create_literal(symtab, e, type.sub_type) }
81
+ # array elements MUST have the same type, so we need to coerce them
82
+ # find the leaf level, and get the bit widths if needed
83
+ ary = Idl::ArrayLiteralAst.new("pruned_literal_ary", 0..18, elements)
84
+ max_element_width = find_max_element_width(symtab, ary)
85
+ if max_element_width.nil?
86
+ ary
87
+ else
88
+ coerce_ary_element_widths(symtab, elements, max_element_width)
89
+ end
90
+ else
91
+ raise "TODO: #{type}"
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ module Idl
98
+ # set up a default
99
+ class AstNode
100
+ # forced_type, when not nil, is the type that the pruned result must be
101
+ # if is used when pruning expressions to ensure that the prune doesn't change
102
+ # bit width just because a value is known and would fit in something smaller
103
+ def prune(symtab, forced_type: nil)
104
+ new_children = children.map { |child| child.prune(symtab, forced_type:) }
105
+
106
+ new_node = dup
107
+ new_node.instance_variable_set(:@children, new_children)
108
+
109
+ if is_a?(Executable)
110
+ value_try do
111
+ execute(symtab)
112
+ end
113
+ # value_else: execute raised ValueError; symtab state is already correct
114
+ end
115
+ add_symbol(symtab) if is_a?(Declaration)
116
+
117
+ new_node
118
+ end
119
+
120
+ def nullify_assignments(symtab)
121
+ children.each { |child| child.nullify_assignments(symtab) }
122
+ end
123
+ end
124
+ class VariableAssignmentAst < AstNode
125
+ def prune(symtab, forced_type: nil)
126
+ new_ast = VariableAssignmentAst.new(input, interval, lhs.dup, rhs.prune(symtab))
127
+ value_try do
128
+ new_ast.execute(symtab)
129
+ end
130
+ # value_else: execute already sets nil on failure, nothing more to do
131
+ new_ast
132
+ end
133
+ def nullify_assignments(symtab)
134
+ sym = symtab.get(lhs.text_value)
135
+ unless sym.nil?
136
+ sym.value = nil
137
+ end
138
+ end
139
+ end
140
+ class AryElementAssignmentAst < AstNode
141
+ def nullify_assignments(symtab)
142
+ case lhs.type(symtab).kind
143
+ when :array
144
+ value_result = value_try do
145
+ lhs_value = lhs.value(symtab)
146
+ value_result2 = value_try do
147
+ lhs_value[idx.value(symtab)] = nil
148
+ end
149
+ value_else(value_result2) do
150
+ # index unknown: nullify entire array
151
+ lhs_value.map! { |_v| nil }
152
+ end
153
+ end
154
+ value_else(value_result) do
155
+ # array var itself is unknown; nothing more to do
156
+ end
157
+ when :bits
158
+ var = symtab.get(lhs.text_value)
159
+ var.value = nil unless var.nil?
160
+ end
161
+ end
162
+ end
163
+ class AryRangeAssignmentAst < AstNode
164
+ def nullify_assignments(symtab)
165
+ return if variable.type(symtab).global?
166
+ var = symtab.get(variable.name)
167
+ var.value = nil unless var.nil?
168
+ end
169
+ end
170
+ class FieldAssignmentAst < AstNode
171
+ def nullify_assignments(symtab)
172
+ var = symtab.get(id.name)
173
+ var.value = nil unless var.nil?
174
+ end
175
+ end
176
+ class MultiVariableAssignmentAst < AstNode
177
+ def nullify_assignments(symtab)
178
+ variables.each do |v|
179
+ sym = symtab.get(v.text_value)
180
+ sym.value = nil unless sym.nil?
181
+ end
182
+ end
183
+ end
184
+ class PostIncrementExpressionAst < AstNode
185
+ def nullify_assignments(symtab)
186
+ var = symtab.get(rval.text_value)
187
+ var.value = nil unless var.nil?
188
+ end
189
+ end
190
+ class PostDecrementExpressionAst < AstNode
191
+ def nullify_assignments(symtab)
192
+ var = symtab.get(rval.text_value)
193
+ var.value = nil unless var.nil?
194
+ end
195
+ end
196
+ class FunctionCallExpressionAst < AstNode
197
+ def prune(symtab, forced_type: nil)
198
+ value_result = value_try do
199
+ v = value(symtab)
200
+ if type(symtab).kind == :bits
201
+ # can only prune if the bit width of the integer is known
202
+ if type(symtab).width == :unknown
203
+ value_error "Unknown width"
204
+ end
205
+ elsif type(symtab).kind == :struct
206
+ value_error <<~MSG
207
+ Literal struct values can't be pruned since a struct can't be initialized with a single expression.
208
+ This would require syntax like { .a = FOO, .b = BAR }
209
+ MSG
210
+ end
211
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
212
+ end
213
+ value_else(value_result) do
214
+ FunctionCallExpressionAst.new(input, interval, name, @children.map { |a| a.prune(symtab) })
215
+ end
216
+ end
217
+ end
218
+ class VariableDeclarationWithInitializationAst < AstNode
219
+ def prune(symtab, forced_type: nil)
220
+ add_symbol(symtab)
221
+
222
+ # do we want to remove a constant? If so, need to add a prune for IdAst that
223
+ # spits out a literal
224
+ #
225
+ # if lhs.const?
226
+ # value_try do
227
+ # rhs.value(symtab)
228
+ # # rhs value is known, and variable is const. it can be removed
229
+ # return NoopAst.new
230
+ # end
231
+ # end
232
+
233
+ VariableDeclarationWithInitializationAst.new(
234
+ input, interval,
235
+ type_name.dup,
236
+ lhs.dup,
237
+ ary_size&.prune(symtab),
238
+ rhs.prune(symtab),
239
+ @for_iter_var
240
+ )
241
+ end
242
+ end
243
+ class ForLoopAst < AstNode
244
+ def prune(symtab, forced_type: nil)
245
+ symtab.push(self)
246
+ symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab)))
247
+
248
+ # Nullify any outer-scope variable assigned in the loop body, since we
249
+ # don't know how many iterations ran (or if any ran at all)
250
+ stmts.each { |stmt| stmt.nullify_assignments(symtab) }
251
+
252
+ # Snapshot after nullification so restore brings back nil values, not pre-loop values
253
+ snapshot = symtab.snapshot_values
254
+
255
+ begin
256
+ new_loop =
257
+ ForLoopAst.new(
258
+ input, interval,
259
+ init.prune(symtab),
260
+ condition.prune(symtab),
261
+ update.prune(symtab),
262
+ stmts.map { |s| s.prune(symtab) }
263
+ )
264
+ ensure
265
+ symtab.restore_values(snapshot)
266
+ symtab.pop
267
+ end
268
+ new_loop
269
+ end
270
+ end
271
+ class FunctionDefAst < AstNode
272
+ def prune(symtab, forced_type: nil)
273
+ pruned_body =
274
+ unless builtin? || generated?
275
+ apply_arg_syms(symtab)
276
+ @body.prune(symtab, args_already_applied: true)
277
+ end
278
+
279
+ FunctionDefAst.new(
280
+ input, interval,
281
+ name,
282
+ @return_type_nodes.map(&:dup),
283
+ @argument_nodes.map(&:dup),
284
+ @desc,
285
+ @type,
286
+ pruned_body
287
+ )
288
+ end
289
+ end
290
+ class ParenExpressionAst
291
+ def prune(symtab, forced_type: nil)
292
+ e = expression.prune(symtab, forced_type:)
293
+ if e.is_a?(ParenExpressionAst)
294
+ e
295
+ elsif e.is_a?(IntLiteralAst) || e.is_a?(TrueExpressionAst) || e.is_a?(FalseExpressionAst) || e.is_a?(IdAst)
296
+ e
297
+ else
298
+ ParenExpressionAst.new(input, interval, e)
299
+ end
300
+ end
301
+ end
302
+ class FunctionBodyAst < AstNode
303
+ def prune(symtab, forced_type: nil, args_already_applied: false)
304
+ symtab.push(self)
305
+
306
+ begin
307
+ func_def = find_ancestor(FunctionDefAst)
308
+ unless args_already_applied || func_def.nil?
309
+
310
+ # push args
311
+ func_def.arguments(symtab).each do |arg_type, arg_name|
312
+ symtab.add(arg_name, Var.new(arg_name, arg_type))
313
+ end
314
+ end
315
+
316
+ pruned_body = nil
317
+
318
+ value_result = value_try do
319
+ # go through the statements, and stop if we find one that returns or raises an exception
320
+ statements.each_with_index do |s, idx|
321
+ if s.is_a?(ReturnStatementAst)
322
+ pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) })
323
+ return pruned_body
324
+ elsif s.is_a?(ConditionalReturnStatementAst)
325
+ value_try do
326
+ v = s.return_value(symtab)
327
+
328
+ # conditional return, condition not taken if v.nil?
329
+ unless v.nil?
330
+ pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) })
331
+ return pruned_body
332
+ end
333
+ end
334
+ # || conditional return, condition not known; keep going
335
+ elsif s.is_a?(StatementAst) && s.action.is_a?(FunctionCallExpressionAst) && s.action.name == "raise"
336
+ pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) })
337
+ return pruned_body
338
+ else
339
+ s.execute(symtab)
340
+ end
341
+ end
342
+
343
+ pruned_body = FunctionBodyAst.new(input, interval, statements.map { |s| s.prune(symtab) })
344
+ end
345
+ value_else(value_result) do
346
+ pruned_body = FunctionBodyAst.new(input, interval, statements.map { |s| s.prune(symtab) })
347
+ end
348
+ ensure
349
+ symtab.pop
350
+ end
351
+
352
+ pruned_body
353
+ end
354
+ end
355
+ class StatementAst < AstNode
356
+ def prune(symtab, forced_type: nil)
357
+ pruned_action = action.prune(symtab)
358
+
359
+ new_stmt = StatementAst.new(input, interval, pruned_action)
360
+ pruned_action.freeze_tree(symtab) unless pruned_action.frozen?
361
+
362
+ pruned_action.add_symbol(symtab) if pruned_action.is_a?(Declaration)
363
+ # action#prune already handles symtab update (execute)
364
+
365
+ new_stmt
366
+ end
367
+ end
368
+ class BinaryExpressionAst < AstNode
369
+ # @!macro prune
370
+ def prune(symtab, forced_type: nil)
371
+ value_try do
372
+ val = value(symtab)
373
+ if val.is_a?(Integer)
374
+ # can only prune if the bit width of the integer is known
375
+ if type(symtab).width == :unknown
376
+ value_error "Unknown width"
377
+ end
378
+ end
379
+ return PruneHelpers.create_literal(symtab, val, type(symtab), forced_type: forced_type || type(symtab))
380
+ end
381
+ # fall through
382
+
383
+ lhs_value = nil
384
+ rhs_value = nil
385
+
386
+ value_try do
387
+ lhs_value = lhs.value(symtab)
388
+ end
389
+
390
+ value_try do
391
+ rhs_value = rhs.value(symtab)
392
+ end
393
+
394
+ if op == "&&"
395
+ raise "pruning error" unless forced_type.nil? || forced_type.kind == :boolean
396
+ if !lhs_value.nil? && !rhs_value.nil?
397
+ PruneHelpers.create_bool_literal(lhs_value && rhs_value)
398
+ elsif lhs_value == true
399
+ rhs.prune(symtab)
400
+ elsif rhs_value == true
401
+ lhs.prune(symtab)
402
+ elsif lhs_value == false || rhs_value == false
403
+ PruneHelpers.create_bool_literal(false)
404
+ else
405
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab))
406
+ end
407
+ elsif op == "||"
408
+ raise "pruning error" unless forced_type.nil? || forced_type.kind == :boolean
409
+ if !lhs_value.nil? && !rhs_value.nil?
410
+ PruneHelpers.create_bool_literal(lhs_value || rhs_value)
411
+ elsif lhs_value == true || rhs_value == true
412
+ PruneHelpers.create_bool_literal(true)
413
+ elsif lhs_value == false
414
+ rhs.prune(symtab)
415
+ elsif rhs_value == false
416
+ lhs.prune(symtab)
417
+ else
418
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab))
419
+ end
420
+ elsif op == "&"
421
+ if lhs_value == 0 && type(symtab).width != :unknown
422
+ PruneHelpers.create_literal(symtab, 0, forced_type: forced_type || type(symtab))
423
+ elsif (rhs.type(symtab).width != :unknown) && lhs_value == ((1 << rhs.type(symtab).width) - 1) && type(symtab).width != :unknown
424
+ # rhs idenntity
425
+ rhs.prune(symtab, forced_type:)
426
+ elsif rhs_value == 0 && type(symtab).width != :unknown
427
+ # anything & 0 == 0
428
+ PruneHelpers.create_literal(symtab, 0, forced_type: forced_type || type(symtab))
429
+ elsif (lhs.type(symtab).width != :unknown) && rhs_value == ((1 << lhs.type(symtab).width) - 1) && type(symtab).width != :unknown
430
+ # lhs identity
431
+ lhs.prune(symtab, forced_type:)
432
+ else
433
+ # neither lhs nor rhs were prunable
434
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab, forced_type:), @op, rhs.prune(symtab, forced_type:))
435
+ end
436
+ elsif op == "|"
437
+ rhs_type = rhs.type(symtab)
438
+ lhs_type = lhs.type(symtab)
439
+
440
+ if lhs_value == 0
441
+ # rhs idenntity
442
+ rhs.prune(symtab, forced_type:)
443
+ elsif rhs_type.width != :unknown && lhs_value == ((1 << rhs.type(symtab).width) - 1) && type(symtab).width != :unknown
444
+ # ~0 | anything == ~0
445
+ PruneHelpers.create_literal(symtab, lhs_value, forced_type: forced_type || type(symtab))
446
+ elsif rhs_value == 0 && type(symtab).width != :unknown
447
+ # lhs identity
448
+ lhs.prune(symtab, forced_type:)
449
+ elsif lhs_type.width != :unknown && rhs_value == ((1 << lhs.type(symtab).width) - 1) && type(symtab).width != :unknown
450
+ # anything | ~0 == ~0
451
+ PruneHelpers.create_literal(symtab, rhs_value, forced_type: forced_type || type(symtab))
452
+ else
453
+ # neither lhs nor rhs were prunable
454
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab, forced_type:), @op, rhs.prune(symtab, forced_type:))
455
+ end
456
+ elsif op == "=="
457
+ if !lhs_value.nil? && !rhs_value.nil?
458
+ PruneHelpers.create_bool_literal(lhs_value == rhs_value)
459
+ else
460
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab))
461
+ end
462
+ else
463
+ BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab))
464
+ end
465
+ end
466
+ end
467
+
468
+ class IfBodyAst < AstNode
469
+ def prune(symtab, restore: true, forced_type: nil)
470
+ pruned_stmts = []
471
+ symtab.push(nil)
472
+ snapshot = symtab.snapshot_values if restore
473
+ stmts.each do |s|
474
+ pruned_stmts << s.prune(symtab)
475
+
476
+ break if pruned_stmts.last.is_a?(StatementAst) && pruned_stmts.last.action.is_a?(FunctionCallExpressionAst) && pruned_stmts.last.action.name == "raise"
477
+ end
478
+ if restore
479
+ symtab.restore_values(snapshot)
480
+ end
481
+ symtab.pop
482
+ IfBodyAst.new(input, interval, pruned_stmts)
483
+ end
484
+ end
485
+
486
+ class ElseIfAst < AstNode
487
+ def prune(symtab, forced_type: nil)
488
+ ElseIfAst.new(
489
+ input, interval,
490
+ body.interval,
491
+ cond.prune(symtab),
492
+ body.prune(symtab).stmts
493
+ )
494
+ end
495
+ end
496
+
497
+ class IfAst < AstNode
498
+ # @!macro prune
499
+ def prune(symtab, forced_type: nil)
500
+ value_result = value_try do
501
+ if if_cond.value(symtab)
502
+ return if_body.prune(symtab, restore: false)
503
+ elsif !elseifs.empty?
504
+ # we know that the if condition is false, so now we treat the else if
505
+ # as the starting point and try again
506
+ return IfAst.new(
507
+ input, interval,
508
+ elseifs[0].cond.dup,
509
+ elseifs[0].body.dup,
510
+ elseifs[1..].map(&:dup),
511
+ final_else_body.dup).prune(symtab)
512
+ elsif !final_else_body.stmts.empty?
513
+ # the if is false, and there are no else ifs, so the result of the prune is just the pruned else body
514
+ return final_else_body.prune(symtab, restore: false)
515
+ else
516
+ # the if is false, and there are no else ifs or elses. This is just a no-op
517
+ return NoopAst.new
518
+ end
519
+ end
520
+ value_else(value_result) do
521
+ # we don't know the value of the if condition
522
+ # we still might know the value of an else if
523
+ unknown_elsifs = []
524
+ elseifs.each do |eif|
525
+ value_result = value_try do
526
+ if eif.cond.value(symtab)
527
+ # this elseif is true, so turn it into an else and then we are done
528
+ return IfAst.new(
529
+ input, interval,
530
+ if_cond.dup,
531
+ if_body.dup,
532
+ unknown_elsifs.map(&:dup),
533
+ eif.body.dup
534
+ ).prune(symtab)
535
+ else
536
+ # this elseif is false, so we can remove it
537
+ next :ok
538
+ end
539
+ end
540
+ value_else(value_result) do
541
+ unknown_elsifs << eif
542
+ end
543
+ end
544
+ # we get here, then we don't know the value of anything. just return this if with everything pruned
545
+ result = IfAst.new(
546
+ input, interval,
547
+ if_cond.prune(symtab),
548
+ if_body.prune(symtab),
549
+ unknown_elsifs.map { |eif| eif.prune(symtab) },
550
+ final_else_body.prune(symtab)
551
+ )
552
+ # Nullify any variable assigned in any branch, since we don't know which ran
553
+ if_body.nullify_assignments(symtab)
554
+ unknown_elsifs.each { |eif| eif.body.nullify_assignments(symtab) }
555
+ final_else_body.nullify_assignments(symtab)
556
+ result
557
+ end
558
+ end
559
+ end
560
+
561
+ class ConditionalReturnStatementAst < AstNode
562
+ def prune(symtab, forced_type: nil)
563
+ value_result = value_try do
564
+ if condition.value(symtab)
565
+ return return_expression.prune(symtab)
566
+ else
567
+ return NoopAst.new
568
+ end
569
+ end
570
+ value_else(value_result) do
571
+ ConditionalReturnStatementAst.new(input, interval, return_expression.prune(symtab), condition.prune(symtab))
572
+ end
573
+ end
574
+ end
575
+
576
+ class ConditionalStatementAst < AstNode
577
+ def prune(symtab, forced_type: nil)
578
+ value_result = value_try do
579
+ if condition.value(symtab)
580
+ pruned_action = action.prune(symtab)
581
+ pruned_action.add_symbol(symtab) if pruned_action.is_a?(Declaration)
582
+ value_result = value_try do
583
+ pruned_action.execute(symtab) if pruned_action.is_a?(Executable)
584
+ end
585
+
586
+ return StatementAst.new(input, interval, pruned_action)
587
+ else
588
+ return NoopAst.new
589
+ end
590
+ end
591
+ value_else(value_result) do
592
+ # condition not known
593
+ pruned_action = action.prune(symtab)
594
+ pruned_action.add_symbol(symtab) if pruned_action.is_a?(Declaration)
595
+ value_result = value_try do
596
+ pruned_action.execute(symtab) if pruned_action.is_a?(Executable)
597
+ end
598
+ # Condition is unknown, so the assignment may not have run; nullify to prevent leakage
599
+ pruned_action.nullify_assignments(symtab)
600
+ ConditionalStatementAst.new(input, interval, pruned_action, condition.prune(symtab))
601
+ end
602
+ end
603
+ end
604
+
605
+ class ConcatenationExpressionAst
606
+ def prune(symtab, forced_type: nil)
607
+ value_result = value_try do
608
+ v = value(symtab)
609
+ return PruneHelpers.create_int_literal(v, forced_type: forced_type || type(symtab))
610
+ end
611
+ value_else(value_result) do
612
+ c = ConcatenationExpressionAst.new(
613
+ input, interval, @children.map { |c| c.prune(symtab) }
614
+ )
615
+ if forced_type
616
+ if forced_type.width < type(symtab).width
617
+ c = AryRangeAccessAst.new(
618
+ input, interval, c, PruneHelpers.create_int_literal(forced_type.width - 1), create_int_literal(0)
619
+ )
620
+ elsif forced_type.width > type(symtab).width
621
+ extra = forced_type.width - type(symtab).width
622
+ mock_type = Struct.new(:width)
623
+ c = ConcatenationExpressionAst.new(
624
+ input, interval, [PruneHelpers.create_int_literal(0, forced_type: mock_type.new(extra))] + @children.map { |c| c.prune(symtab) }
625
+ )
626
+ end
627
+ end
628
+ c
629
+ end
630
+ end
631
+ end
632
+
633
+ class ReplicationExpressionAst
634
+ def prune(symtab, forced_type: nil)
635
+ value_result = value_try do
636
+ v = value(symtab)
637
+ return PruneHelpers.create_int_literal(v, forced_type: forced_type || type(symtab))
638
+ end
639
+ value_else(value_result) do
640
+ c = ReplicationExpressionAst.new(input, interval, n.prune(symtab), v.prune(symtab))
641
+ if forced_type
642
+ if forced_type.width < type(symtab).width
643
+ c = AryRangeAccessAst.new(
644
+ input, interval, c, PruneHelpers.create_int_literal(forced_type.width - 1), create_int_literal(0)
645
+ )
646
+ elsif forced_type.width > type(symtab).width
647
+ extra = forced_type.width - type(symtab).width
648
+ mock_type = Struct.new(:width)
649
+ c = ConcatenationExpressionAst.new(
650
+ input, interval, [PruneHelpers.create_int_literal(0, forced_type: mock_type.new(extra))] + @children.map { |c| c.prune(symtab) }
651
+ )
652
+ end
653
+ end
654
+ c
655
+ end
656
+ end
657
+ end
658
+
659
+ class IntLiteralAst
660
+ def prune(symtab, forced_type: nil)
661
+ if forced_type
662
+ raise "pruning error: attempt to force bitwidth when width is unknown" if forced_type.width.nil? || forced_type.width == :unknown
663
+ s = "#{forced_type.width}'d#{value(symtab)}"
664
+ IntLiteralAst.new(s, 0...s.size, s)
665
+ else
666
+ dup
667
+ end
668
+ end
669
+ end
670
+
671
+ class TernaryOperatorExpressionAst < AstNode
672
+ def prune(symtab, forced_type: nil)
673
+ value_result = value_try do
674
+ if condition.value(symtab)
675
+ return true_expression.prune(symtab, forced_type: forced_type || type(symtab))
676
+ else
677
+ return false_expression.prune(symtab, forced_type: forced_type || type(symtab))
678
+ end
679
+ end
680
+ value_else(value_result) do
681
+ TernaryOperatorExpressionAst.new(
682
+ input, interval,
683
+ condition.prune(symtab),
684
+ true_expression.prune(symtab),
685
+ false_expression.prune(symtab)
686
+ )
687
+ end
688
+ end
689
+ end
690
+
691
+ class CsrFieldAssignmentAst < AstNode
692
+ def prune(symtab, forced_type: nil)
693
+ CsrFieldAssignmentAst.new(input, interval, csr_field.dup, write_value.prune(symtab))
694
+ end
695
+ end
696
+
697
+ class CsrFieldReadExpressionAst < AstNode
698
+ def prune(symtab, forced_type: nil)
699
+ value_result = value_try do
700
+ v = value(symtab)
701
+ if type(symtab).width == :unknown
702
+ value_error "unknown width"
703
+ end
704
+ return PruneHelpers.create_int_literal(v, forced_type: forced_type || type(symtab))
705
+ end
706
+ value_else(value_result) do
707
+ CsrFieldReadExpressionAst.new(input, interval, @csr.dup, @field_name)
708
+ end
709
+ end
710
+ end
711
+
712
+ class CsrReadExpressionAst < AstNode
713
+ def prune(symtab, forced_type: nil)
714
+ value_result = value_try do
715
+ v = value(symtab)
716
+ if type(symtab).width == :unknown
717
+ value_error "unknown width"
718
+ end
719
+ return PruneHelpers.create_int_literal(v, forced_type: forced_type || type(symtab))
720
+ end
721
+ value_else(value_result) do
722
+ CsrReadExpressionAst.new(input, interval, @csr_name)
723
+ end
724
+ end
725
+ end
726
+
727
+ class BitsCastAst < AstNode
728
+ def prune(symtab, forced_type: nil)
729
+ p = expr.prune(symtab, forced_type:)
730
+ if p.type(symtab).kind == :bits
731
+ return p
732
+ else
733
+ return BitsCastAst.new(input, interval, p)
734
+ end
735
+ end
736
+ end
737
+
738
+ class IdAst < AstNode
739
+ def prune(symtab, forced_type: nil)
740
+ value_result = value_try do
741
+ value_error "Not pruning struct types" if type(symtab).kind == :struct
742
+ v = value(symtab)
743
+ if type(symtab).kind == :bits
744
+ if type(symtab).width == :unknown
745
+ value_error "Unknown width"
746
+ end
747
+ end
748
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
749
+ end
750
+ value_else(value_result) do
751
+ dup
752
+ end
753
+ end
754
+ end
755
+
756
+ class UnaryOperatorExpressionAst < AstNode
757
+ def prune(symtab, forced_type: nil)
758
+ value_result = value_try do
759
+ v = value(symtab)
760
+ if type(symtab).kind == :bits
761
+ if type(symtab).width == :unknown
762
+ value_error "Unknown width"
763
+ end
764
+ end
765
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
766
+ end
767
+ value_else(value_result) do
768
+ UnaryOperatorExpressionAst.new(input, interval, @op, exp.prune(symtab, forced_type:))
769
+ end
770
+ end
771
+ end
772
+
773
+ class AryElementAccessAst < AstNode
774
+ def prune(symtab, forced_type: nil)
775
+ value_result = value_try do
776
+ v = value(symtab)
777
+ if type(symtab).kind == :bits
778
+ if type(symtab).width == :unknown
779
+ value_error "Unknown width"
780
+ end
781
+ end
782
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
783
+ end
784
+ value_else(value_result) do
785
+ AryElementAccessAst.new(input, interval, var.prune(symtab), index.prune(symtab))
786
+ end
787
+ end
788
+ end
789
+
790
+ class AryRangeAccessAst < AstNode
791
+ def prune(symtab, forced_type: nil)
792
+ value_result = value_try do
793
+ v = value(symtab)
794
+ if type(symtab).width == :unknown
795
+ value_error "Unknown width"
796
+ end
797
+ return PruneHelpers.create_int_literal(v, forced_type: forced_type || type(symtab))
798
+ end
799
+ value_else(value_result) do
800
+ AryRangeAccessAst.new(input, interval, var.prune(symtab), msb.prune(symtab), lsb.prune(symtab))
801
+ end
802
+ end
803
+ end
804
+
805
+ class FieldAccessExpressionAst < AstNode
806
+ def prune(symtab, forced_type: nil)
807
+ value_result = value_try do
808
+ v = value(symtab)
809
+ if type(symtab).kind == :bits
810
+ if type(symtab).width == :unknown
811
+ value_error "Unknown width"
812
+ end
813
+ end
814
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
815
+ end
816
+ value_else(value_result) do
817
+ FieldAccessExpressionAst.new(input, interval, obj.prune(symtab), @field_name)
818
+ end
819
+ end
820
+ end
821
+
822
+ class EnumRefAst < AstNode
823
+ def prune(symtab, forced_type: nil)
824
+ value_result = value_try do
825
+ v = value(symtab)
826
+ return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
827
+ end
828
+ value_else(value_result) do
829
+ dup
830
+ end
831
+ end
832
+ end
833
+
834
+ class ReturnStatementAst < AstNode
835
+ def prune(symtab, forced_type: nil)
836
+ ReturnStatementAst.new(input, interval, return_expression.prune(symtab))
837
+ end
838
+ end
839
+
840
+ class ReturnExpressionAst < AstNode
841
+ def prune(symtab, forced_type: nil)
842
+ ReturnExpressionAst.new(input, interval, return_value_nodes.map { |n| n.prune(symtab) })
843
+ end
844
+ end
845
+
846
+ class MultiVariableAssignmentAst < AstNode
847
+ def prune(symtab, forced_type: nil)
848
+ new_ast = MultiVariableAssignmentAst.new(
849
+ input, interval,
850
+ variables.map(&:dup),
851
+ function_call.prune(symtab)
852
+ )
853
+ value_try do
854
+ new_ast.execute(symtab)
855
+ end
856
+ # value_else: execute already sets nil on failure, nothing more to do
857
+ new_ast
858
+ end
859
+ end
860
+
861
+ class AryElementAssignmentAst < AstNode
862
+ def prune(symtab, forced_type: nil)
863
+ new_ast = AryElementAssignmentAst.new(
864
+ input, interval,
865
+ lhs.dup,
866
+ idx.prune(symtab),
867
+ rhs.prune(symtab)
868
+ )
869
+ value_try do
870
+ new_ast.execute(symtab)
871
+ end
872
+ # value_else: execute already sets nil on failure, nothing more to do
873
+ new_ast
874
+ end
875
+ end
876
+
877
+ class AryRangeAssignmentAst < AstNode
878
+ def prune(symtab, forced_type: nil)
879
+ new_ast = AryRangeAssignmentAst.new(
880
+ input, interval,
881
+ variable.dup,
882
+ msb.prune(symtab),
883
+ lsb.prune(symtab),
884
+ write_value.prune(symtab)
885
+ )
886
+ value_try do
887
+ new_ast.execute(symtab)
888
+ end
889
+ # value_else: execute already sets nil on failure, nothing more to do
890
+ new_ast
891
+ end
892
+ end
893
+
894
+ class FieldAssignmentAst < AstNode
895
+ def prune(symtab, forced_type: nil)
896
+ new_ast = FieldAssignmentAst.new(
897
+ input, interval,
898
+ id.dup,
899
+ @field_name,
900
+ rhs.prune(symtab)
901
+ )
902
+ value_try do
903
+ new_ast.execute(symtab)
904
+ end
905
+ # value_else: execute already sets nil on failure, nothing more to do
906
+ new_ast
907
+ end
908
+ end
909
+
910
+ class VariableDeclarationAst < AstNode
911
+ def prune(symtab, forced_type: nil)
912
+ add_symbol(symtab)
913
+ dup
914
+ end
915
+ end
916
+
917
+ class MultiVariableDeclarationAst < AstNode
918
+ def prune(symtab, forced_type: nil)
919
+ add_symbol(symtab)
920
+ dup
921
+ end
922
+ end
923
+
924
+ class PostIncrementExpressionAst < AstNode
925
+ def prune(symtab, forced_type: nil)
926
+ new_ast = PostIncrementExpressionAst.new(input, interval, rval.dup)
927
+ value_try do
928
+ new_ast.execute(symtab)
929
+ end
930
+ # value_else: execute already sets nil on failure, nothing more to do
931
+ new_ast
932
+ end
933
+ end
934
+
935
+ class PostDecrementExpressionAst < AstNode
936
+ def prune(symtab, forced_type: nil)
937
+ new_ast = PostDecrementExpressionAst.new(input, interval, rval.dup)
938
+ value_try do
939
+ new_ast.execute(symtab)
940
+ end
941
+ # value_else: execute already sets nil on failure, nothing more to do
942
+ new_ast
943
+ end
944
+ end
945
+
946
+ class PcAssignmentAst < AstNode
947
+ def prune(symtab, forced_type: nil)
948
+ PcAssignmentAst.new(input, interval, rhs.prune(symtab))
949
+ end
950
+ end
951
+
952
+ class CsrSoftwareWriteAst < AstNode
953
+ def prune(symtab, forced_type: nil)
954
+ CsrSoftwareWriteAst.new(input, interval, csr.dup, expression.prune(symtab))
955
+ end
956
+ end
957
+ end