Class: Idl::FieldAssignmentAst

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

Overview

represents a bitfield or struct assignment

for example:

Sv39PageTableEntry entry;
entry.PPN = 0

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

Methods included from Executable

#executable?

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, #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, id, field_name, rhs) ⇒ FieldAssignmentAst

Returns a new instance of FieldAssignmentAst.



3250
3251
3252
3253
# File 'lib/idlc/ast.rb', line 3250

def initialize(input, interval, id, field_name, rhs)
  super(input, interval, [id, rhs])
  @field_name = field_name
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
# File 'lib/idlc/ast.rb', line 3337

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

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  FieldAssignmentAst.new(
    input, interval,
    T.cast(AstNode.from_h(yaml.fetch("var"), source_mapper), IdAst),
    yaml.fetch("field_name"),
    T.cast(AstNode.from_h(yaml.fetch("value"), source_mapper), RvalueAst)
  )
end

Instance Method Details

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
# File 'lib/idlc/ast.rb', line 3235

def const_eval?(symtab)
  var = symtab.get(id.name)
  type_error "#{id.name} is not declared!" if var.nil?

  return false if !var.const_eval?

  if rhs.const_eval?(symtab)
    true
  else
    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



3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
# File 'lib/idlc/ast.rb', line 3295

def execute(symtab)
  var = symtab.get(id.name)
  type_error "#{id.name} has not been declared" if var.nil?

  if var.type.kind == :bitfield
    bitfield_val = id.value(symtab)
    range = var.type.range(@field_name)
    rhs_value = T.cast(rhs.value(symtab), Integer)
    rhs_value_trunc = rhs_value & ((1 << range.size) - 1)

    # zero out the field
    bitfield_val &= ~(((1 << range.size) - 1) << range.first)
    bitfield_val |= rhs_value_trunc << range.first
    var.value = bitfield_val
  elsif var.type.kind == :struct
    struct_val = id.value(symtab)
    value_result = value_try do
      struct_val[@field_name] = rhs.value(symtab)
    end
    value_else(value_result) do
      struct_val[@field_name] = nil
      value_error ""
    end
  else
    value_error "TODO: Field assignment execution"
  end
end

#field_nameObject



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

def field_name = @field_name

#gen_adoc(indent, indent_spaces: 2) ⇒ Object



97
98
99
# File 'lib/idlc/passes/gen_adoc.rb', line 97

def gen_adoc(indent, indent_spaces: 2)
  "#{id.gen_adoc(0, indent_spaces:)}.#{@field_name} = #{rhs.gen_adoc(0, indent_spaces:)}"
end

#idObject



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

def id = T.cast(@children.fetch(0), IdAst)

#nullify_assignments(symtab) ⇒ Object



179
180
181
182
# File 'lib/idlc/passes/prune.rb', line 179

def nullify_assignments(symtab)
  var = symtab.get(id.name)
  var.value = nil unless var.nil?
end

#prune(symtab, forced_type: nil) ⇒ Object



931
932
933
934
935
936
937
938
939
940
941
942
943
# File 'lib/idlc/passes/prune.rb', line 931

def prune(symtab, forced_type: nil)
  new_ast = FieldAssignmentAst.new(
    input, interval,
    id.dup,
    @field_name,
    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



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

def rhs = T.cast(@children.fetch(1), RvalueAst)

#to_hObject



3328
3329
3330
3331
3332
3333
3334
# File 'lib/idlc/ast.rb', line 3328

def to_h = {
  "kind" => "field_assignment",
  "var" => id.to_h,
  "field_name" => @field_name,
  "value" => rhs.to_h,
  "source" => source_yaml
}

#to_idlObject



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

def to_idl = "#{id.to_idl}.#{@field_name} = #{rhs.to_idl}"

#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

Parameters:

Returns:

  • (Type)

    The type of the node

Raises:



3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
# File 'lib/idlc/ast.rb', line 3256

def type(symtab)
  var = symtab.get(id.name)
  type_error "#{id.name} has not been declared" if var.nil?

  if var.type.kind == :bitfield
    Type.new(:bits, width: var.type.range(@field_name).size)
  elsif var.type.kind == :struct
    var.type.member_type(@field_name)
  else
    internal_error "huh? #{id.text_value} #{var.type.kind}"
  end
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

Parameters:

Raises:



3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
# File 'lib/idlc/ast.rb', line 3270

def type_check(symtab, strict:)
  id.type_check(symtab, strict:)
  var = symtab.get(id.name)
  type_error "#{id.name} has not been declared" if var.nil?

  if var.type.kind == :bitfield
    internal_error "#{id.name} Not a BitfieldType (is a #{var.type})" unless var.type.respond_to?(:field_names)
    unless var.type.field_names.include?(@field_name)
      type_error "#{@field_name} is not a member of #{var.type}"
    end
  elsif var.type.kind == :struct
    type_error "#{@field_name} is not a member of #{var.type}" unless var.type.member?(@field_name)
  else
    type_error "#{id.name} is not a bitfield  or struct (is #{var.type})"
  end

  type_error "Cannot write const variable" if var.type.const?

  rhs.type_check(symtab, strict:)
  return if rhs.type(symtab).convertable_to?(type(symtab))

  type_error "Incompatible type in assignment (#{type(symtab)}, #{rhs.type(symtab)})"
end