Class: Idl::AryElementAssignmentAst

Inherits:
AstNode
  • Object
show all
Includes:
Executable
Defined in:
lib/idlc/ast.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/gen_adoc.rb,
lib/idlc/passes/find_src_registers.rb

Overview

represents an array element assignment

for example:

X[rs1] = XLEN'd0

Constant Summary

Constants inherited from AstNode

Idl::AstNode::Bits1Type, Idl::AstNode::Bits32Type, Idl::AstNode::Bits64Type, Idl::AstNode::BoolType, Idl::AstNode::ConstBoolType, Idl::AstNode::PossiblyUnknownBits1Type, Idl::AstNode::PossiblyUnknownBits32Type, Idl::AstNode::PossiblyUnknownBits64Type, Idl::AstNode::ReachableFunctionCacheType, Idl::AstNode::StringType, Idl::AstNode::VoidType

Instance Attribute Summary

Attributes inherited from AstNode

#children, #input, #interval, #parent

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Executable

#executable?

Methods inherited from AstNode

#always_terminates?, #declaration?, #executable?, extract_base_var_name, #find_ancestor, #find_referenced_csrs, #find_src_registers, #freeze_tree, #gen_option_adoc, #input_file, input_from_source_yaml, #inspect, #internal_error, interval_from_source_yaml, #lineno, #lines_around, #pass_find_return_values, #path, #print_ast, #reachable_exceptions, #reachable_functions, #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, lhs, idx, rhs) ⇒ AryElementAssignmentAst

Returns a new instance of AryElementAssignmentAst.



2952
2953
2954
# File 'lib/idlc/ast.rb', line 2952

def initialize(input, interval, lhs, idx, rhs)
  super(input, interval, [lhs, idx, rhs])
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
# File 'lib/idlc/ast.rb', line 3036

def self.from_h(yaml, source_mapper)
  raise "Bad YAML" unless yaml.key?("kind") && yaml.fetch("kind") == "array_element_assignment"

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  AryElementAssignmentAst.new(
    input, interval,
    T.cast(AstNode.from_h(yaml.fetch("array"), source_mapper), RvalueAst),
    T.cast(AstNode.from_h(yaml.fetch("index"), source_mapper), RvalueAst),
    T.cast(AstNode.from_h(yaml.fetch("value"), source_mapper), RvalueAst)
  )
end

Instance Method Details

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
# File 'lib/idlc/ast.rb', line 2932

def const_eval?(symtab)
  return false if !lhs.const_eval?(symtab)

  if idx.const_eval?(symtab) && rhs.const_eval?(symtab)
    true
  else
    base_name = AstNode.extract_base_var_name(lhs)
    type_error "Cannot determine base variable for #{lhs.text_value}" if base_name.nil?
    lhs_var = symtab.get(base_name)
    type_error "array #{base_name} has not been declared" if lhs_var.nil?

    lhs_var.const_incompatible!
    false
  end
end

#execute(symtab) ⇒ void

This method returns an undefined value.

“execute” the statement by updating the variables in the symbol table

Parameters:

  • symtab (SymbolTable)

    The symbol table for the context

Raises:

  • ValueError if some part of the statement cannot be executed at compile time



2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
# File 'lib/idlc/ast.rb', line 2991

def execute(symtab)
  lhs_type = lhs.type(symtab)
  return if lhs_type.global?

  case lhs_type.kind
  when :array
    idx_value = idx.value(symtab)
    lhs_value = lhs.value(symtab)
    value_result = value_try do
      lhs_value[idx_value] = rhs.value(symtab)
    end
    value_else(value_result) do
      lhs_value[idx_value] = nil
      value_error "right-hand side of array element assignment is unknown"
    end
  when :bits
    value_result = value_try do
      new_element = (lhs.value(symtab) & ~0) | ((rhs.value(symtab) & 1) << idx.value(symtab))
      AstNode.write_back_nested(lhs, new_element, symtab)
    end
    value_else(value_result) do
      base_name = T.must(AstNode.extract_base_var_name(lhs))
      v = symtab.get(base_name)
      internal_error "did not find array base '#{base_name}'" if v.nil?
      v.value = nil
    end
  else
    internal_error "unexpected type for array element assignment"
  end
end

#find_dst_registers(symtab) ⇒ Object



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
133
134
135
136
137
138
139
# File 'lib/idlc/passes/find_src_registers.rb', line 108

def find_dst_registers(symtab)
  # Identify the base variable and the register index based on assignment shape.
  # F[rd] = v    → lhs is IdAst(F),              reg_idx = idx
  # F[rd][b] = v → lhs is AryElementAccessAst(F[rd]), reg_idx = lhs.index
  lhs_base, reg_idx =
    if lhs.is_a?(Idl::IdAst)
      [lhs, idx]
    elsif lhs.is_a?(Idl::AryElementAccessAst) && lhs.var.is_a?(Idl::IdAst)
      [lhs.var, lhs.index]
    else
      return []
    end

  # Only proceed if the base variable is a global array of RegFileElementType.
  var_type = lhs_base.type(symtab) rescue nil
  return [] unless var_type&.kind == :array &&
                   var_type.sub_type.is_a?(RegFileElementType) &&
                   var_type.qualifiers.include?(:global)

  rf_name = var_type.sub_type.name

  value_result = value_try do
    return [[rf_name, reg_idx.value(symtab)]]
  end
  value_else(value_result) do
    if reg_idx.type(symtab).const?
      return [[rf_name, reg_idx.gen_cpp(symtab, 0)]]
    else
      raise ComplexRegDetermination
    end
  end
end

#gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object



258
259
260
# File 'lib/idlc/passes/gen_adoc.rb', line 258

def gen_adoc(indent = 0, indent_spaces: 2)
  "#{' ' * indent}#{lhs.gen_adoc(0, indent_spaces:)}[#{idx.gen_adoc(0, indent_spaces:)}] = #{rhs.gen_adoc(0, indent_spaces:)}"
end

#idxObject



2949
# File 'lib/idlc/ast.rb', line 2949

def idx = @children[1]

#lhsObject



2948
# File 'lib/idlc/ast.rb', line 2948

def lhs = @children[0]

#nullify_assignments(symtab) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/idlc/passes/prune.rb', line 145

def nullify_assignments(symtab)
  case lhs.type(symtab).kind
  when :array
    value_result = value_try do
      lhs_value = lhs.value(symtab)
      value_result2 = value_try do
        lhs_value[idx.value(symtab)] = nil
      end
      value_else(value_result2) do
        # index unknown: nullify entire array
        lhs_value.map! { |_v| nil }
      end
    end
    value_else(value_result) do
      # array var itself is unknown; nothing more to do
    end
  when :bits
    root = lhs
    root = root.var while root.is_a?(AryElementAccessAst) || root.is_a?(AryRangeAccessAst)
    var = symtab.get(root.name)
    var.value = nil unless var.nil?
  end
end

#prune(symtab, forced_type: nil) ⇒ Object



898
899
900
901
902
903
904
905
906
907
908
909
910
# File 'lib/idlc/passes/prune.rb', line 898

def prune(symtab, forced_type: nil)
  new_ast = AryElementAssignmentAst.new(
    input, interval,
    lhs.dup,
    idx.prune(symtab),
    rhs.prune(symtab)
  )
  value_try do
    new_ast.execute(symtab)
  end
  # value_else: execute already sets nil on failure, nothing more to do
  new_ast
end

#rhsObject



2950
# File 'lib/idlc/ast.rb', line 2950

def rhs = @children[2]

#to_hObject



3027
3028
3029
3030
3031
3032
3033
# File 'lib/idlc/ast.rb', line 3027

def to_h = {
  "kind" => "array_element_assignment",
  "array" => lhs.to_h,
  "index" => idx.to_h,
  "value" => rhs.to_h,
  "source" => source_yaml
}

#to_idlObject



3024
# File 'lib/idlc/ast.rb', line 3024

def to_idl = "#{lhs.to_idl}[#{idx.to_idl}] = #{rhs.to_idl}"

#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

Parameters:

Raises:



2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
# File 'lib/idlc/ast.rb', line 2957

def type_check(symtab, strict:)
  lhs.type_check(symtab, strict:)
  unless [:array, :bits].include?(lhs.type(symtab).kind)
    type_error "#{lhs.text_value} must be an array or an integral type"
  end
  type_error "Assigning to a constant" if lhs.type(symtab).const?

  idx.type_check(symtab, strict:)

  type_error "Index must be integral" unless idx.type(symtab).integral?

  value_result = value_try do
    idx_value = idx.value(symtab)
    type_error "Array index (#{idx.text_value} = #{idx_value}) out of range (< #{lhs.type(symtab).width})" if idx_value >= lhs.type(symtab).width
  end
  # OK, doesn't need to be known

  rhs.type_check(symtab, strict:)

  case lhs.type(symtab).kind
  when :array
    unless rhs.type(symtab).convertable_to?(lhs.type(symtab).sub_type)
      type_error "Incompatible type in array assignment"
    end
  when :bits
    unless rhs.type(symtab).convertable_to?(Bits1Type)
      type_error "Incompatible type in integer slice assignment"
    end
  else
    internal_error "Unexpected type on array element assignment"
  end
end