Class: Idl::IntLiteralAst

Inherits:
AstNode
  • Object
show all
Includes:
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

Overview

represents an integer literal

Defined Under Namespace

Classes: Memo

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 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, #reachable_exceptions, #reachable_functions, #set_input_file, #set_input_file_unless_already_set, #source_line_file_offsets, #source_starting_offset, #source_yaml, #starting_line, #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, text) ⇒ IntLiteralAst

Returns a new instance of IntLiteralAst.



7494
7495
7496
7497
7498
# File 'lib/idlc/ast.rb', line 7494

def initialize(input, interval, text)
  @text = text
  super(input, interval, EMPTY_ARRAY)
  @memo = Memo.new
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
# File 'lib/idlc/ast.rb', line 7807

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

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  text = "#{yaml.fetch("width")}'#{yaml.fetch("signed") ? "s" : ""}#{radix_to_verilog(yaml.fetch("radix"))}#{yaml.fetch("value").to_s(yaml.fetch("radix"))}"
  IntLiteralAst.new(
    input, interval, text
  )
end

.radix_to_verilog(r) ⇒ Object



7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
# File 'lib/idlc/ast.rb', line 7795

def self.radix_to_verilog(r)
  case r
  when 2 then "b"
  when 8 then "o"
  when 10 then "d"
  when 16 then "h"
  else
    raise "bad radix: #{r}"
  end
end

Instance Method Details

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


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

def const_eval?(symtab) = true

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



147
148
149
150
# File 'lib/idlc/passes/gen_adoc.rb', line 147

def gen_adoc(indent = 0, indent_spaces: 2)
  raise "?" if text_value.empty?
  "#{' ' * indent}#{text_value}"
end

#gen_option_adocObject



103
104
105
106
107
108
109
110
111
# File 'lib/idlc/passes/gen_option_adoc.rb', line 103

def gen_option_adoc
  if value(nil) == 1 << 65
    "UNDEFINED_LEGAL"
  elsif value(nil) == 1 << 66
    "UNDEFINED_LEGAL_DETERMINISTIC"
  else
    text_value
  end
end

#prune(symtab, forced_type: nil) ⇒ Object



694
695
696
697
698
699
700
701
702
# File 'lib/idlc/passes/prune.rb', line 694

def prune(symtab, forced_type: nil)
  if forced_type
    raise "pruning error: attempt to force bitwidth when width is unknown" if forced_type.width.nil? || forced_type.width == :unknown
    s = "#{forced_type.width}'d#{value(symtab)}"
    IntLiteralAst.new(s, 0...s.size, s)
  else
    dup
  end
end

#radixObject



7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
# File 'lib/idlc/ast.rb', line 7746

def radix
  case text_value.delete("_")
  when /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    r = T.must(::Regexp.last_match(5))
    if r.empty?
      10
    elsif r == "b"
      2
    elsif r == "o"
      8
    elsif r == "d"
      10
    elsif r == "h"
      16
    else
      raise "unhandled literal"
    end
  when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/
    r = T.must(::Regexp.last_match(1))
    if r.empty?
      10
    elsif r == "b"
      2
    elsif r == "d"
      10
    elsif r == "x"
      16
    else
      raise "unhandled literal: #{r}"
    end
  when /^([0-9]*)(s?)$/
    10
  else
    raise "unhandled literal"
  end
end

#signed?Boolean

Returns:

  • (Boolean)


7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
# File 'lib/idlc/ast.rb', line 7729

def signed?
  case text_value.delete("_")
  when /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    signed = T.must(::Regexp.last_match(4))
    !signed.empty?
  when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/
    signed = T.must(::Regexp.last_match(3))
    !signed.empty?
  when /^([0-9]*)(s?)$/
    signed = T.must(::Regexp.last_match(2))
    !signed.empty?
  else
    raise "unhandled literal"
  end
end

#text_valueObject



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

def text_value = @text

#to_hObject



7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
# File 'lib/idlc/ast.rb', line 7784

def to_h
  {
    "kind" => "bits_literal",
    "width" => (width(nil) == :unknown) ? "MXLEN" : width(nil).to_s,
    "value" => unsigned_value,
    "signed" => signed?,
    "radix" => radix,
    "source" => source_yaml
  }
end

#to_idlObject



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

def to_idl = text_value

#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:



7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
# File 'lib/idlc/ast.rb', line 7519

def type(symtab)
  return @memo.type unless @memo.type.nil?

  case text_value.delete("_")
  when /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    # verilog-style literal
    signed = ::Regexp.last_match(4)
    value = ::Regexp.last_match(6)

    width = width(symtab)

    unless width == :unknown
      type_error("integer width must be positive (is #{width})") unless width.is_a?(Integer) && width.positive?
    end

    qualifiers = signed == "s" ? [:signed, :const] : [:const]
    qualifiers << :known unless T.must(value).include?("x")
    @memo.type =
      if width == :unknown
        Type.new(:bits, width:, max_width: 64, qualifiers:)
      else
        Type.new(:bits, width:, qualifiers:)
      end
  when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/
    # C++-style literal
    signed = ::Regexp.last_match(3)

    qualifiers = signed == "s" ? [:signed, :const, :known] : [:const, :known]
    @memo.type = Type.new(:bits, width: width(symtab), qualifiers:)
  when /^([0-9]*)(s?)$/
    # basic decimal
    signed = ::Regexp.last_match(2)

    qualifiers = signed == "s" ? [:signed, :const, :known] : [:const, :known]
    @memo.type = Type.new(:bits, width: width(symtab), qualifiers:)
  else
    internal_error "Unhandled int value '#{text_value}'"
  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:



7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
# File 'lib/idlc/ast.rb', line 7503

def type_check(symtab, strict:)
  if text_value.delete("_") =~ /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    # verilog-style literal
    width = ::Regexp.last_match(1)
    value_text = ::Regexp.last_match(6)

    if width.nil? || width == "MXLEN"
      width = symtab.mxlen.nil? ? 32 : symtab.mxlen # 32 is the min width, which is what we care about here
    end

    # ensure we actually have enough bits to represent the value
    type_error("#{value_text} cannot be represented in #{width} bits") if unsigned_value.bit_length > width.to_i
  end
end

#unsigned_valueInteger

Returns the unsigned value of this literal (i.e., treating it as unsigned even if the signed specifier is present).

Returns:

  • (Integer)

    the unsigned value of this literal (i.e., treating it as unsigned even if the signed specifier is present)



7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
# File 'lib/idlc/ast.rb', line 7645

def unsigned_value
  return @memo.unsigned_value unless @memo.unsigned_value.nil?

  @memo.unsigned_value =
    case text_value.delete("_")
    when /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
      # verilog-style literal
      radix_id = T.must(::Regexp.last_match(5))
      value = T.must(::Regexp.last_match(6))

      radix_id = "d" if radix_id.empty?

      if value.index("x").nil? && value.index("X").nil?
        case radix_id
        when "b"
          value.to_i(2)
        when "o"
          value.to_i(8)
        when "d"
          value.to_i(10)
        when "h"
          value.to_i(16)
        end
      else
        # there is unknown bit(s) in the value
        known_value =
          case radix_id
          when "b"
            value.gsub(/[xX]/, "0").to_i(2)
          when "o"
            value.gsub(/[xX]/, "0").to_i(8)
          when "d"
            raise "impossible"
          when "h"
            value.gsub(/[xX]/, "0").to_i(16)
          end
        unknown_mask =
          case radix_id
          when "b"
            value.gsub("1", "0").gsub(/[xX]/, "1").to_i(2)
          when "o"
            value.gsub(/[0-7]/, "0").gsub(/[xX]/, "7").to_i(8)
          when "d"
            raise "impossible"
          when "h"
            value.gsub(/[0-9a-fA-F]/, "0").gsub(/[xX]/, "f").to_i(16)
          end
        UnknownLiteral.new(known_value, unknown_mask)
      end
    when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/
      # C++-style literal
      radix_id = T.must(::Regexp.last_match(1))
      value = T.must(::Regexp.last_match(2))

      radix_id = "o" if radix_id.empty?

      # @unsigned_value =
      case radix_id
      when "b"
        value.to_i(2)
      when "o"
        value.to_i(8)
      when "d"
        value.to_i(10)
      when "x"
        value.to_i(16)
      end

    when /^([0-9]*)(s?)$/
      # basic decimal
      value = T.must(::Regexp.last_match(1))

      # @unsigned_value = value.to_i(10)
      value.to_i(10)
    else
      internal_error "Unhandled int value '#{text_value}'"
    end
end

#value(symtab) ⇒ Object

Return the compile-time-known value of the node



7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
# File 'lib/idlc/ast.rb', line 7598

def value(symtab)
  return @memo.value unless @memo.value.nil?

  if text_value.delete("_") =~ /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    # verilog-style literal
    signed = T.must(::Regexp.last_match(4))
    width = width(symtab)

    v =
      if width == :unknown || width == "MXLEN"
        if !signed.empty?
          if unsigned_value > 0x7fff_ffff
            value_error("Don't know if value will be negative")
          else
            if unsigned_value > 0xffff_ffff
              value_error("Don't know if value will fit in literal")
            end
            unsigned_value
          end
        else
          if unsigned_value > 0xffff_ffff
            value_error("Don't know if value will fit in literal")
          end
          unsigned_value
        end
      else
        if unsigned_value.bit_length > width
          value_error("Value does not fit in literal")
        end
        if !signed.empty? && ((unsigned_value >> (width - 1)) == 1)
          if unsigned_value.bit_length > (width - 1)
            value_error("Value does not fit in literal")
          end
          -(2**width.to_i - unsigned_value)
        else
          unsigned_value
        end
      end

    @memo.value = v
  else
    @memo.value = unsigned_value
  end
end

#width(symtab) ⇒ Object



7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
# File 'lib/idlc/ast.rb', line 7559

def width(symtab)
  return @memo.width unless @memo.width.nil?

  text_value_no_underscores = text_value.delete("_")

  case text_value_no_underscores
  when /^((MXLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/
    # verilog-style literal
    width = ::Regexp.last_match(1)
    if width.nil? || width == "MXLEN"
      if symtab.nil?
        width = :unknown
      else
        width = symtab.mxlen.nil? ? :unknown : symtab.mxlen
      end
    else
      width = width.to_i
    end
    @memo.width = width
  when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/
    signed = ::Regexp.last_match(3)

    width = signed == "s" ? value(symtab).bit_length + 1 : value(symtab).bit_length
    width = 1 if width.zero? # happens when the literal is '0'

    @memo.width = width
  when /^([0-9]*)(s?)$/
    signed = ::Regexp.last_match(3)

    width = signed == "s" ? value(symtab).bit_length + 1 : value(symtab).bit_length
    width = 1 if width.zero? # happens when the literal is '0'

    @memo.width = width
  else
    internal_error "No match on int literal"
  end
end