Class: Idl::FunctionCallExpressionAst
- Inherits:
-
AstNode
- Object
- AstNode
- Idl::FunctionCallExpressionAst
show all
- Includes:
- Executable, Rvalue
- Defined in:
- lib/idlc/ast.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/gen_adoc.rb,
lib/idlc/passes/gen_option_adoc.rb,
lib/idlc/passes/reachable_functions.rb,
lib/idlc/passes/reachable_exceptions.rb
Constant Summary
Constants inherited
from AstNode
AstNode::Bits1Type, AstNode::Bits32Type, AstNode::Bits64Type, AstNode::BoolType, AstNode::ConstBoolType, AstNode::PossiblyUnknownBits1Type, AstNode::PossiblyUnknownBits32Type, AstNode::PossiblyUnknownBits64Type, AstNode::ReachableFunctionCacheType, AstNode::StringType, AstNode::VoidType
Instance Attribute Summary
Attributes inherited from AstNode
#children, #input, #interval, #parent
Class Method Summary
collapse
Instance Method Summary
collapse
-
#arg_nodes ⇒ Array<AstNode>
-
#const_eval?(symtab) ⇒ Boolean
-
#execute(symtab) ⇒ Object
-
#func_type(symtab) ⇒ Object
-
#gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object
-
#gen_option_adoc ⇒ Object
-
#initialize(input, interval, function_name, args) ⇒ FunctionCallExpressionAst
constructor
A new instance of FunctionCallExpressionAst.
-
#name ⇒ Object
-
#prune(symtab, forced_type: nil) ⇒ Object
-
#reachable_exceptions(symtab, cache = {}) ⇒ Object
-
#reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType)) ⇒ Object
-
#to_h ⇒ Object
-
#to_idl ⇒ Object
-
#type(symtab) ⇒ Type
Given a specific symbol table, return the type of this node.
-
#type_check(symtab, strict:) ⇒ void
type check this node and all children.
-
#value(symtab) ⇒ Object
Return the compile-time-known value of the node.
Methods included from Executable
#executable?
Methods included from Rvalue
#max_value, #min_value, #truncate, #values
Methods inherited from AstNode
#always_terminates?, #declaration?, #executable?, extract_base_var_name, #find_ancestor, #find_dst_registers, #find_referenced_csrs, #find_src_registers, #freeze_tree, #input_file, input_from_source_yaml, #inspect, #internal_error, interval_from_source_yaml, #lineno, #lines_around, #nullify_assignments, #pass_find_return_values, #path, #print_ast, #set_input_file, #set_input_file_unless_already_set, #source_line_file_offsets, #source_starting_offset, #source_yaml, #starting_line, #text_value, #truncation_warn, #type_error, #unindent, value_else, #value_else, value_error, #value_error, value_try, #value_try, write_back_nested
Constructor Details
#initialize(input, interval, function_name, args) ⇒ FunctionCallExpressionAst
Returns a new instance of FunctionCallExpressionAst.
7839
7840
7841
7842
7843
7844
7845
7846
7847
|
# File 'lib/idlc/ast.rb', line 7839
def initialize(input, interval, function_name, args)
raise ArgumentError, "args should be an array" unless args.is_a?(Array)
super(input, interval, args)
@name = function_name
@reachable_exceptions_func_call_cache = {}
@func_def_type_cache = {}
end
|
Class Method Details
.from_h(yaml, source_mapper) ⇒ Object
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
|
# File 'lib/idlc/ast.rb', line 8002
def self.from_h(yaml, source_mapper)
raise "Bad YAML" unless yaml.key?("kind") && yaml.fetch("kind") == "funcall_expr"
input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
interval = interval_from_source_yaml(yaml.fetch("source"))
FunctionCallExpressionAst.new(
input, interval,
yaml.fetch("func"),
yaml.fetch("args").map { |a| AstNode.from_h(a, source_mapper) }
)
end
|
Instance Method Details
#arg_nodes ⇒ Array<AstNode>
Returns Function argument nodes.
7850
7851
7852
|
# File 'lib/idlc/ast.rb', line 7850
def arg_nodes
@children
end
|
#const_eval?(symtab) ⇒ Boolean
7834
7835
7836
7837
|
# File 'lib/idlc/ast.rb', line 7834
def const_eval?(symtab)
@children.all? { |arg| arg.const_eval?(symtab) } && \
func_type(symtab).func_def_ast.const_eval?(symtab)
end
|
#execute(symtab) ⇒ Object
7979
|
# File 'lib/idlc/ast.rb', line 7979
def execute(symtab) = value(symtab)
|
#func_type(symtab) ⇒ Object
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
|
# File 'lib/idlc/ast.rb', line 7854
def func_type(symtab)
func_def_type = @func_def_type_cache[symtab.name]
return func_def_type unless func_def_type.nil?
func_def_type = symtab.get(@name)
type_error "No symbol #{@name}" if func_def_type.nil?
unless func_def_type.is_a?(FunctionType)
type_error "#{@name} is not a function (it's a #{func_def_type.class.name})"
end
@func_def_type_cache[symtab.name] = func_def_type
end
|
#gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object
294
295
296
297
|
# File 'lib/idlc/passes/gen_adoc.rb', line 294
def gen_adoc(indent = 0, indent_spaces: 2)
func_link = "%%UDB_DOC_LINK%func;#{name};#{name}%%"
"#{' ' * indent}" + func_link + "pass:[(]#{arg_nodes.map { |a| a.gen_adoc(0, indent_spaces:) }.join(', ')})"
end
|
#gen_option_adoc ⇒ Object
28
29
30
|
# File 'lib/idlc/passes/gen_option_adoc.rb', line 28
def gen_option_adoc
gen_adoc(0)
end
|
#name ⇒ Object
7981
7982
7983
|
# File 'lib/idlc/ast.rb', line 7981
def name
@name
end
|
#prune(symtab, forced_type: nil) ⇒ Object
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
# File 'lib/idlc/passes/prune.rb', line 205
def prune(symtab, forced_type: nil)
value_result = value_try do
v = value(symtab)
if type(symtab).kind == :bits
if type(symtab).width == :unknown
value_error "Unknown width"
end
elsif type(symtab).kind == :struct
value_error <<~MSG
Literal struct values can't be pruned since a struct can't be initialized with a single expression.
This would require syntax like { .a = FOO, .b = BAR }
MSG
end
return PruneHelpers.create_literal(symtab, v, type(symtab), forced_type: forced_type || type(symtab))
end
value_else(value_result) do
FunctionCallExpressionAst.new(input, interval, name, @children.map { |a| a.prune(symtab) })
end
end
|
#reachable_exceptions(symtab, cache = {}) ⇒ Object
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
|
# File 'lib/idlc/passes/reachable_exceptions.rb', line 25
def reachable_exceptions(symtab, cache = {})
if name == "raise" || name == "raise_precise"
code_ast = arg_nodes[0]
value_result = value_try do
code = code_ast.value(symtab)
internal_error "Code should be an integer" unless code.is_a?(Integer)
return 1 << code
end
value_else(value_result) do
value_error "Cannot determine value of exception code"
end
end
func_def_type = func_type(symtab)
mask = 0
arg_nodes.each do |a|
mask |= a.reachable_exceptions(symtab, cache) if a.is_a?(FunctionCallExpressionAst)
end
unless func_def_type.builtin? || func_def_type.generated?
body_symtab = symtab.global_clone
body_symtab.push(func_def_type.func_def_ast)
avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self)
idx = [name, [], avals].hash
begin
body_mask =
if cache.key?(idx)
cache[idx]
else
cache[idx] = func_def_type.body.reachable_exceptions(body_symtab, cache)
end
mask |= body_mask
ensure
body_symtab.pop
body_symtab.release
end
end
mask
end
|
#reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType)) ⇒ Object
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
|
# File 'lib/idlc/passes/reachable_functions.rb', line 36
def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
func_def_type = func_type(symtab)
body_symtab = symtab.global_clone
body_symtab.push(func_def_type.func_def_ast)
fns_by_name = {}
begin
arg_nodes.each do |a|
a.reachable_functions(symtab, cache).each { |fn| fns_by_name[fn.name] ||= fn }
end
unless func_def_type.builtin? || func_def_type.generated?
avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self)
idx = [name, avals].hash
if cache.key?(idx)
cache[idx].each { |fn| fns_by_name[fn.name] ||= fn }
else
cache[idx] = [] body_fns = func_def_type.body.reachable_functions(body_symtab, cache)
cache[idx] = body_fns
body_fns.each { |fn| fns_by_name[fn.name] ||= fn }
end
end
fns_by_name[func_def_type.func_def_ast.name] ||= func_def_type.func_def_ast
ensure
body_symtab.pop
body_symtab.release
end
fns_by_name.values
end
|
#to_h ⇒ Object
7992
7993
7994
7995
7996
7997
7998
7999
|
# File 'lib/idlc/ast.rb', line 7992
def to_h
{
"kind" => "funcall_expr",
"func" => name,
"args" => arg_nodes.map(&:to_h),
"source" => source_yaml
}
end
|
#to_idl ⇒ Object
7987
7988
7989
|
# File 'lib/idlc/ast.rb', line 7987
def to_idl
"#{name}(#{arg_nodes.map(&:to_idl).join(',')})"
end
|
#type(symtab) ⇒ Type
Given a specific symbol table, return the type of this node.
Should not be called until #type_check is called with the same arguments
7902
7903
7904
7905
7906
7907
7908
|
# File 'lib/idlc/ast.rb', line 7902
def type(symtab)
return ConstBoolType if name == "implemented?" || name == "implemented_version?" || name == "implemented_csr?"
rtype = func_type(symtab).return_type(arg_nodes, self)
rtype = rtype.make_const if arg_nodes.all? { |a| a.type(symtab).const? } && func_type(symtab).func_def_ast.const_eval?(symtab)
rtype
end
|
#type_check(symtab, strict:) ⇒ void
This method returns an undefined value.
type check this node and all children
Calls to #type and/or #value may depend on type_check being called first with the same symtab. If not, those functions may raise an AstNode::InternalError
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
|
# File 'lib/idlc/ast.rb', line 7869
def type_check(symtab, strict:)
type_error "Cannot use reserved word '#{@name}' as function name" if ReservedWords::RESERVED.include?(@name)
level = symtab.levels
func_def_type = func_type(symtab)
num_args = arg_nodes.size
if func_def_type.num_args != num_args
type_error "Wrong number of arguments to '#{name}' function call. Expecting #{func_def_type.num_args}, got #{num_args}"
end
arg_nodes.each do |a|
a.type_check(symtab, strict:)
end
arg_nodes.each_with_index do |a, idx|
unless a.type(symtab).convertable_to?(func_def_type.argument_type(idx, arg_nodes, symtab, self))
type_error "Wrong type for argument number #{idx + 1}. Expecting #{func_def_type.argument_type(idx, [], arg_nodes, symtab, self)}, got #{a.type(symtab)}"
end
arg_name = func_def_type.argument_nodes.fetch(idx).name
if arg_name[0].upcase == arg_name[0]
unless a.type(symtab).const?
type_error "You cannot pass a mutable expression to '#{arg_name}' const argument of #{name}"
end
end
end
if func_def_type.return_type(arg_nodes, self).nil?
internal_error "No type determined for function"
end
internal_error "Function call symtab not at same level post type check (#{symtab.levels} #{level})" unless symtab.levels == level
end
|
#value(symtab) ⇒ Object
Return the compile-time-known value of the node
7911
7912
7913
7914
7915
7916
7917
7918
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
|
# File 'lib/idlc/ast.rb', line 7911
def value(symtab)
if name == "xlen" && !symtab.get("__effective_xlen").nil?
return symtab.get("__effective_xlen").value
end
func_def_type = func_type(symtab)
type_error "#{name} is not a function" unless func_def_type.is_a?(FunctionType)
if func_def_type.generated?
value_error "builtin functions not provided" if symtab.builtin_funcs.nil?
if name == "implemented?"
extname_ref = arg_nodes[0]
type_error "First argument should be a ExtensionName" unless extname_ref.type(symtab).kind == :enum_ref && extname_ref.class_name == "ExtensionName"
v = symtab.builtin_funcs.implemented.call(extname_ref.member_name)
if v.nil?
value_error "implemented? is only known when evaluating in the context of a fully-configured arch def"
end
return v
elsif name == "implemented_version?"
extname_ref = arg_nodes[0]
type_error "First argument should be a ExtensionName" unless extname_ref.type(symtab).kind == :enum_ref && extname_ref.class_name == "ExtensionName"
ver_req = arg_nodes[1].text_value[1..-2]
v = symtab.builtin_funcs.implemented_version.call(extname_ref.member_name, ver_req)
if v.nil?
value_error "implemented_version? is only known when evaluating in the context of a fully-configured arch def"
end
return v
elsif name == "implemented_csr?"
csr_addr = arg_nodes[0].value(symtab)
v = symtab.builtin_funcs.implemented_csr.call(csr_addr)
if v.nil?
value_error "implemented_csr? is only known when evaluating in the context of a fully-configured arch def"
end
return v
elsif name == "cached_translation"
value_error "cached_translation is not compile-time-knowable"
elsif name == "maybe_cache_translation"
value_error "maybe_cache_translation is not compile-time-knowable"
elsif name == "invalidate_translations"
value_error "invalidate_translations is not compile-time-knowable"
elsif name == "direct_csr_lookup"
value_error "direct_csr_lookup is not compile-time-knowable"
elsif name == "indirect_csr_lookup"
value_error "indirect_csr_lookup is not compile-time-knowable"
elsif name == "csr_hw_read"
value_error "csr_hw_read is not compile-time-knowable"
elsif name == "csr_sw_read"
value_error "csr_sw_read is not compile-time-knowable"
elsif name == "csr_sw_write"
value_error "csr_sw_write is not compile-time-knowable"
else
internal_error "Unimplemented generated: '#{name}'"
end
end
if func_def_type.builtin?
value_error "value of builtin functions aren't knowable"
end
func_def_type.return_value(arg_nodes, symtab, self)
end
|