Class: Instruction

Inherits:
ArchDefObject show all
Defined in:
lib/arch_obj_models/instruction.rb

Overview

model of a specific instruction in a specific base (RV32/RV64)

Defined Under Namespace

Classes: DecodeVariable, Encoding, EncodingField

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, arch_def) ⇒ Instruction

Returns a new instance of Instruction.



217
218
219
220
# File 'lib/arch_obj_models/instruction.rb', line 217

def initialize(data, arch_def)
  super(data)
  @arch_def = arch_def
end

Instance Attribute Details

#arch_defArchDef (readonly)

Returns The architecture definition.

Returns:

  • (ArchDef)

    The architecture definition



215
216
217
# File 'lib/arch_obj_models/instruction.rb', line 215

def arch_def
  @arch_def
end

Instance Method Details

#<=>(other) ⇒ Object



20
21
22
23
24
25
26
# File 'lib/arch_obj_models/instruction.rb', line 20

def <=>(other)
  if other.is_a?(Instruction)
    name <=> other.name
  else
    raise ArgumentError, "Instruction is not comparable to a #{other.class.name}"
  end
end

#==(other) ⇒ Object Also known as: eql?



10
11
12
13
14
15
16
# File 'lib/arch_obj_models/instruction.rb', line 10

def ==(other)
  if other.is_a?(Instruction)
    name == other.name
  else
    raise ArgumentError, "Instruction is not comparable to a #{other.class.name}"
  end
end

#accessHash<String, String>

Returns Hash of access permissions for each mode. The key is the lowercase name of a privilege mode, and the value is one of [‘never’, ‘sometimes’, ‘always’].

Returns:

  • (Hash<String, String>)

    Hash of access permissions for each mode. The key is the lowercase name of a privilege mode, and the value is one of [‘never’, ‘sometimes’, ‘always’]



29
30
31
# File 'lib/arch_obj_models/instruction.rb', line 29

def access
  @data["access"]
end

#access_detailString?

Returns:

  • (String)

    Details of the access restrictions

  • (nil)

    if no details are available



35
36
37
# File 'lib/arch_obj_models/instruction.rb', line 35

def access_detail
  @data["access_detail"]
end

#access_detail?Boolean

Returns true if the instruction has an ‘access_detail’ field.

Returns:

  • (Boolean)

    true if the instruction has an ‘access_detail’ field



636
637
638
# File 'lib/arch_obj_models/instruction.rb', line 636

def access_detail?
  @data.key?("access_detail")
end

#assemblyString

Returns Assembly format.

Returns:

  • (String)

    Assembly format



55
56
57
# File 'lib/arch_obj_models/instruction.rb', line 55

def assembly
  @data["assembly"]
end

#baseInteger?

Returns:

  • (Integer)

    XLEN that must be effective for instruction to exist

  • (nil)

    if instruction exists in all XLENs



41
42
43
# File 'lib/arch_obj_models/instruction.rb', line 41

def base
  @data["base"]
end

#data_independent_timing?Boolean

Returns Whether or not the instruction must have data-independent timing when Zkt is enabled.

Returns:

  • (Boolean)

    Whether or not the instruction must have data-independent timing when Zkt is enabled.



46
# File 'lib/arch_obj_models/instruction.rb', line 46

def data_independent_timing? = @data["data_independent_timing"]

#decode_variables(base) ⇒ Array<DecodeVariable>

Returns The decode variables.

Returns:



631
632
633
# File 'lib/arch_obj_models/instruction.rb', line 631

def decode_variables(base)
  encoding(base).decode_variables
end

#defined_in_base?(xlen) ⇒ Boolean

Returns whethen or not instruction is defined in base xlen.

Parameters:

  • xlen (Integer)

    32 or 64, the target xlen

Returns:

  • (Boolean)

    whethen or not instruction is defined in base xlen



50
51
52
# File 'lib/arch_obj_models/instruction.rb', line 50

def defined_in_base?(xlen)
  base == xlen
end

#encoding(base) ⇒ Encoding

Returns the encoding.

Parameters:

  • base (Integer)

    32 or 64

Returns:



617
618
619
620
621
# File 'lib/arch_obj_models/instruction.rb', line 617

def encoding(base)
  load_encoding if @encodings.nil?

  @encodings[base]
end

#encoding_widthInteger

Returns the width of the encoding.

Returns:

  • (Integer)

    the width of the encoding



624
625
626
627
628
# File 'lib/arch_obj_models/instruction.rb', line 624

def encoding_width
  raise "unexpected: encodings are different sizes" unless encoding(32).size == encoding(64).size

  encoding(64).size
end

#excluded_by?(ext_name, ext_version) ⇒ Boolean #excluded_by?(ext_version) ⇒ Boolean

Overloads:

  • #excluded_by?(ext_name, ext_version) ⇒ Boolean

    Returns Whether or not the instruction is excluded by extesion ‘ext`, version `version`.

    Parameters:

    • ext_name (#to_s)

      An extension name

    • ext_version (#to_s)

      A specific extension version

    Returns:

    • (Boolean)

      Whether or not the instruction is excluded by extesion ‘ext`, version `version`

  • #excluded_by?(ext_version) ⇒ Boolean

    Returns Whether or not the instruction is excluded by ext_version.

    Parameters:

    Returns:

    • (Boolean)

      Whether or not the instruction is excluded by ext_version



675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/arch_obj_models/instruction.rb', line 675

def excluded_by?(*args)
  return false if @data["excludedBy"].nil?

  excluded_by = SchemaCondition.new(@data["excludedBy"])

  if args.size == 1
    raise ArgumentError, "Parameter must be an ExtensionVersion" unless args[0].is_a?(ExtensionVersion)

    excluded_by.satisfied_by? do |r|
      r.name == args[0].name && r.version_requirement.satisfied_by?(args[0].version)
    end
  elsif args.size == 2
    raise ArgumentError, "First parameter must be an extension name" unless args[0].respond_to?(:to_s)
    raise ArgumentError, "Second parameter must be an extension version" unless args[0].respond_to?(:to_s)

    version = args[1].is_a?(Gem::Version) ? args[1] : Gem::Version.new(args[1])

    excluded_by.satisfied_by? do |r|
      r.name == args[0] && r.version_requirement.satisfied_by?(version)
    end
  end
end

#exists_in_cfg?(arch_def) ⇒ Boolean

Returns whether or not the instruction is implemented given the supplies config options.

Parameters:

  • arch_def (ArchDef)

    The architecture definition

Returns:

  • (Boolean)

    whether or not the instruction is implemented given the supplies config options



700
701
702
703
704
705
706
707
708
709
710
711
712
# File 'lib/arch_obj_models/instruction.rb', line 700

def exists_in_cfg?(arch_def)
  if arch_def.fully_configured?
    (@data["base"].nil? || (arch_def.possible_xlens.include? @data["base"])) &&
      arch_def.implemented_extensions.any? { |e| defined_by?(e) } &&
      arch_def.implemented_extensions.none? { |e| excluded_by?(e) }
  else
    raise "unexpected arch_def type" unless arch_def.partially_configured?

    (@data["base"].nil? || (arch_def.possible_xlens.include? @data["base"])) &&
      arch_def.prohibited_extensions.none? { |e| defined_by?(e) } &&
      arch_def.mandatory_extensions.none? { |e| excluded_by?(e) }
  end
end

#mask_to_array(int) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/arch_obj_models/instruction.rb', line 143

def mask_to_array(int)
  elems = []
  idx = 0
  while int != 0
    if (int & (1 << idx)) != 0
      elems << idx
    end
    int &= ~(1 << idx)
    idx += 1
  end
  elems
end

#multi_encoding?Boolean

Returns whether or not this instruction has different encodings depending on XLEN.

Returns:

  • (Boolean)

    whether or not this instruction has different encodings depending on XLEN



574
575
576
# File 'lib/arch_obj_models/instruction.rb', line 574

def multi_encoding?
  @data.key?("encoding") && @data["encoding"].key?("RV32")
end

#operation_ast(symtab) ⇒ FunctionBodyAst

Returns The abstract syntax tree of the instruction operation.

Parameters:

  • symtab (SymbolTable)

    Symbol table with compilation context

Returns:

  • (FunctionBodyAst)

    The abstract syntax tree of the instruction operation



598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
# File 'lib/arch_obj_models/instruction.rb', line 598

def operation_ast(symtab)
  return @operation_ast unless @operation_ast.nil?
  return nil if @data["operation()"].nil?

  # now, parse the operation
  @operation_ast = symtab.archdef.idl_compiler.compile_inst_operation(
    self,
    symtab:,
    input_file: @data["__source"],
    input_line: source_line("operation()")
  )

  raise "unexpected #{@operation_ast.class}" unless @operation_ast.is_a?(Idl::FunctionBodyAst)

  @operation_ast
end

#pruned_operation_ast(global_symtab, effective_xlen) ⇒ Idl::FunctionBodyAst

Returns A pruned abstract syntax tree.

Parameters:

  • global_symtab (Idl::SymbolTable)

    Symbol table with global scope populated and a configuration loaded

Returns:

  • (Idl::FunctionBodyAst)

    A pruned abstract syntax tree



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/arch_obj_models/instruction.rb', line 85

def pruned_operation_ast(global_symtab, effective_xlen)
  @pruned_asts ||= {}

  arch_def = global_symtab.archdef

  pruned_ast = @pruned_asts[arch_def.name]
  return pruned_ast unless pruned_ast.nil?

  return nil unless @data.key?("operation()")

  type_checked_ast = type_checked_operation_ast(arch_def.idl_compiler, global_symtab, effective_xlen)
  print "Pruning #{name} operation()..."
  pruned_ast = type_checked_ast.prune(fill_symtab(global_symtab, effective_xlen, type_checked_ast))
  puts "done"
  pruned_ast.freeze_tree(global_symtab)
  arch_def.idl_compiler.type_check(
    pruned_ast,
    fill_symtab(global_symtab, effective_xlen, pruned_ast),
    "#{name}.operation() (pruned)"
  )

  @pruned_asts[arch_def.name] = pruned_ast
end

#reachable_exceptions(symtab, effective_xlen) ⇒ Integer

Returns Mask of all exceptions that can be reached from operation().

Parameters:

  • symtab (Idl::SymbolTable)

    Symbol table with global scope populated

  • effective_xlen (Integer)

    Effective XLEN to evaluate against

Returns:

  • (Integer)

    Mask of all exceptions that can be reached from operation()



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/arch_obj_models/instruction.rb', line 131

def reachable_exceptions(symtab, effective_xlen)
  if @data["operation()"].nil?
    []
  else
    # pruned_ast =  pruned_operation_ast(symtab)
    # type_checked_operation_ast()
    type_checked_ast = type_checked_operation_ast(symtab.arch_def.idl_compiler, symtab, effective_xlen)
    symtab = fill_symtab(symtab, effective_xlen, pruned_ast)
    type_checked_ast.reachable_exceptions(symtab)
  end
end

#reachable_exceptions_str(symtab, effective_xlen = nil) ⇒ Array<Integer>

Returns List of all exceptions that can be reached from operation().

Parameters:

  • symtab (Idl::SymbolTable)

    Symbol table with global scope populated

  • effective_xlen (Integer) (defaults to: nil)

    Effective XLEN to evaluate against. If nil, evaluate against all valid XLENs

Returns:

  • (Array<Integer>)

    List of all exceptions that can be reached from operation()



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/arch_obj_models/instruction.rb', line 159

def reachable_exceptions_str(symtab, effective_xlen=nil)
  if @data["operation()"].nil?
    []
  else
    etype = symtab.get("ExceptionCode")
    if effective_xlen.nil?
      if symtab.archdef.multi_xlen?
        if base.nil?
          (
            pruned_ast = pruned_operation_ast(symtab, 32)
            print "Determining reachable exceptions from #{name}#RV32..."
            e32 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 32, pruned_ast))).map { |code|
              etype.element_name(code)
            }
            puts "done"
            pruned_ast = pruned_operation_ast(symtab, 64)
            print "Determining reachable exceptions from #{name}#RV64..."
            e64 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 64, pruned_ast))).map { |code|
              etype.element_name(code)
            }
            puts "done"
            e32 + e64
          ).uniq
        else
          pruned_ast = pruned_operation_ast(symtab, base)
          print "Determining reachable exceptions from #{name}..."
          e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, base, pruned_ast))).map { |code|
            etype.element_name(code)
          }
          puts "done"
          e
        end
      else
        effective_xlen = symtab.archdef.mxlen
        pruned_ast = pruned_operation_ast(symtab, effective_xlen)
        print "Determining reachable exceptions from #{name}..."
        e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, pruned_ast))).map { |code|
          etype.element_name(code)
        }
        puts "done"
        e
      end
    else
      pruned_ast = pruned_operation_ast(symtab, effective_xlen)

      print "Determining reachable exceptions from #{name}..."
      e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, pruned_ast))).map { |code|
        etype.element_name(code)
      }
      puts "done"
      e
    end
  end
end

#reachable_functions(symtab, effective_xlen) ⇒ Array<Idl::FunctionBodyAst>

Returns List of all functions that can be reached from operation().

Parameters:

  • symtab (Idl::SymbolTable)

    Symbol table with global scope populated

  • effective_xlen (Integer)

    The effective XLEN to evaluate against

Returns:

  • (Array<Idl::FunctionBodyAst>)

    List of all functions that can be reached from operation()



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/arch_obj_models/instruction.rb', line 112

def reachable_functions(symtab, effective_xlen)
  if @data["operation()"].nil?
    []
  else
    # RubyProf.start
    ast = type_checked_operation_ast(symtab.archdef.idl_compiler, symtab, effective_xlen)
    print "Determining reachable funcs from #{name}..."
    fns = ast.reachable_functions(fill_symtab(symtab, effective_xlen, ast))
    puts "done"
    # result = RubyProf.stop
    # RubyProf::FlatPrinter.new(result).print($stdout)
    # exit
    fns
  end
end

#rv32?Boolean

Returns whether or not this instruction is defined for RV32.

Returns:

  • (Boolean)

    whether or not this instruction is defined for RV32



659
660
661
# File 'lib/arch_obj_models/instruction.rb', line 659

def rv32?
  !@data.key?("base") || base == 32
end

#rv64?Boolean

Returns whether or not this instruction is defined for RV64.

Returns:

  • (Boolean)

    whether or not this instruction is defined for RV64



664
665
666
# File 'lib/arch_obj_models/instruction.rb', line 664

def rv64?
  !@data.key?("base") || base == 64
end

#type_checked_operation_ast(idl_compiler, symtab, effective_xlen) ⇒ FunctionBodyAst

Returns A type-checked abstract syntax tree of the operation.

Parameters:

  • idl_compiler (Idl::Compiler)

    Compiler

  • symtab (Idl::SymbolTable)

    Symbol table with globals

  • effective_xlen (Integer)

    32 or 64, the effective xlen to type check against

Returns:

  • (FunctionBodyAst)

    A type-checked abstract syntax tree of the operation



582
583
584
585
586
587
588
589
590
591
592
593
594
# File 'lib/arch_obj_models/instruction.rb', line 582

def type_checked_operation_ast(idl_compiler, symtab, effective_xlen)
  @type_checked_operation_ast ||= {}
  ast = @type_checked_operation_ast[symtab.hash]
  return ast unless ast.nil?

  return nil unless @data.key?("operation()")

  ast = operation_ast(symtab)

  idl_compiler.type_check(ast, fill_symtab(symtab, effective_xlen, ast), "#{name}.operation()")

  @type_checked_operation_ast[symtab.hash] = ast
end

#wavedrom_desc(base) ⇒ String

Generates a wavedrom description of the instruction encoding

Parameters:

  • base (Integer)

    The XLEN (32 or 64), needed if the instruction is #multi_encoding?

Returns:

  • (String)

    The wavedrom JSON description



644
645
646
647
648
649
650
651
652
653
654
655
656
# File 'lib/arch_obj_models/instruction.rb', line 644

def wavedrom_desc(base)
  desc = {
    "reg" => []
  }
  display_fields = encoding(base).opcode_fields
  display_fields += encoding(base).decode_variables.map(&:grouped_encoding_fields).flatten

  display_fields.sort { |a, b| b.range.last <=> a.range.last }.reverse.each do |e|
    desc["reg"] << { "bits" => e.range.size, "name" => e.name, "type" => (e.opcode? ? 2 : 4) }
  end

  desc
end