Class: Udb::Instruction

Inherits:
TopLevelDatabaseObject show all
Includes:
Helpers::WavedromUtil
Defined in:
lib/udb/obj/instruction.rb

Overview

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

Defined Under Namespace

Classes: DecodeVariable, Encoding, EncodingField, MemoizedState, Opcode

Instance Attribute Summary

Attributes inherited from DatabaseObject

#arch, #data, #data_path, #long_name, #name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from TopLevelDatabaseObject

create_json_schemer_resolver, #key?, #keys

Methods inherited from DatabaseObject

#__source, #cfg_arch, #cfg_arch?, #clone, #defer, #defined_by_condition, #description, #inspect, #kind, #source_line

Constructor Details

#initialize(data, data_path, arch) ⇒ Instruction

Returns a new instance of Instruction.



119
120
121
122
# File 'lib/udb/obj/instruction.rb', line 119

def initialize(data, data_path, arch)
  super(data, data_path, arch)
  @memo = MemoizedState.new
end

Class Method Details

.ary_from_location(location_str_or_int) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/udb/obj/instruction.rb', line 259

def self.ary_from_location(location_str_or_int)
  return [location_str_or_int] if location_str_or_int.is_a?(Integer)

  bits = []
  parts = location_str_or_int.split("|")
  parts.each do |part|
    if part.include?("-")
      msb, lsb = part.split("-").map(&:to_i)
      (lsb..msb).each { |i| bits << i }
    else
      bits << part.to_i
    end
  end
  bits
end

.deprecated_validate_encoding(encoding, inst_name) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/udb/obj/instruction.rb', line 296

def self.deprecated_validate_encoding(encoding, inst_name)
  match = encoding["match"]
  raise "No match for instruction #{inst_name}?" if match.nil?

  variables = encoding.key?("variables") ? encoding["variables"] : []
  match.size.times do |i|
    if match[match.size - 1 - i] == "-"
      # make sure exactly one variable covers this bit
      vars_match = variables.count { |variable| ary_from_location(variable["location"]).include?(i) }
      if vars_match.zero?
        raise ValidationError, "In instruction #{inst_name}, no variable or encoding bit covers bit #{i}"
      elsif vars_match != 1
        raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by more than one variable"
      end
    else
      # make sure no variable covers this bit
      unless variables.nil?
        unless variables.none? { |variable| ary_from_location(variable["location"]).include?(i) }
          raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by both a variable and the match string"
        end
      end
    end
  end
end

.validate_encoding(inst, base) ⇒ Object



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/udb/obj/instruction.rb', line 276

def self.validate_encoding(inst, base)
  # make sure there is no overlap between variables/opcodes
  (inst.opcodes(base) + inst.decode_variables(base)).combination(2) do |field1, field2|
    raise "In instruction #{inst.name}, #{field1.name} and #{field2.name} overlap" if field1.overlaps?(field2)
  end

  # makes sure every bit is accounted for
  inst.type(base).length.times do |i|
    covered =
      inst.opcodes(base).any? { |opcode| opcode.range.cover?(i) } || \
      inst.decode_variables(base).any? { |var| var.location_bits.include?(i) }
    raise "In instruction #{inst.name}, there is no opcode or variable at bit #{i}" unless covered
  end

  # make sure opcode values fit
  inst.opcodes(base).each do |opcode|
    raise "In instruction #{inst.name}, opcode #{opcode.name}, value #{opcode.value} does not fit in #{opcode.range}" unless T.must(opcode.range.size) >= opcode.value.bit_length
  end
end

Instance Method Details

#<=>(other) ⇒ Object



368
369
370
371
372
373
374
# File 'lib/udb/obj/instruction.rb', line 368

def <=>(other)
  if other.is_a?(Instruction)
    name <=> other.name
  else
    nil
  end
end

#==(other) ⇒ Object



360
361
362
363
364
365
366
# File 'lib/udb/obj/instruction.rb', line 360

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’]



377
378
379
# File 'lib/udb/obj/instruction.rb', line 377

def access
  @data["access"]
end

#access_detailString?

Returns:

  • (String)

    Details of the access restrictions

  • (nil)

    if no details are available



383
384
385
# File 'lib/udb/obj/instruction.rb', line 383

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



1177
1178
1179
# File 'lib/udb/obj/instruction.rb', line 1177

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

#assemblyString

Returns Assembly format.

Returns:

  • (String)

    Assembly format



411
412
413
# File 'lib/udb/obj/instruction.rb', line 411

def assembly
  @data["assembly"]
end

#bad_encoding_conflict?(xlen, other_inst) ⇒ Boolean

Returns true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design.

Returns:

  • (Boolean)

    true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design



1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
# File 'lib/udb/obj/instruction.rb', line 1061

def bad_encoding_conflict?(xlen, other_inst)
  return false if !defined_in_base?(xlen) || !other_inst.defined_in_base?(xlen)
  return false unless encoding(xlen).indistinguishable?(other_inst.encoding(xlen))

  # ok, so they have the same encoding. can they be present at the same time?
  return false if !defined_by_condition.compatible?(other_inst.defined_by_condition)

  # is this a hint?
  !(hints.include?(other_inst) || other_inst.hints.include?(self))
end

#baseObject



388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/udb/obj/instruction.rb', line 388

def base
  return @base if defined?(@base)

  @base =
    if defined_by_condition.rv32_only?
      32
    elsif defined_by_condition.rv64_only?
      64
    else
      nil
    end
end

#conflicting_instructions(xlen) ⇒ Array<Instruction>

Returns List of instructions that reuse this instruction’s encoding, but can’t be present in the same system because their defining extensions conflict.

Returns:

  • (Array<Instruction>)

    List of instructions that reuse this instruction’s encoding, but can’t be present in the same system because their defining extensions conflict



1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
# File 'lib/udb/obj/instruction.rb', line 1075

def conflicting_instructions(xlen)
  raise "Bad xlen (#{xlen}) for instruction #{name}" unless defined_in_base?(xlen)

  @conflicting_instructions ||= {}
  return @conflicting_instructions[xlen] unless @conflicting_instructions[xlen].nil?

  @conflicting_instructions[xlen] = []

  @arch.instructions.each do |other_inst|
    next unless other_inst.defined_in_base?(xlen)
    next if other_inst == self

    next unless encoding(xlen).indistinguishable?(other_inst.encoding(xlen))

    # is this a hint?
    next if hints.include?(other_inst) || other_inst.hints.include?(self)

    if defined_by_condition.compatible?(other_inst.defined_by_condition)
      raise "bad encoding conflict found between #{name} and #{other_inst.name}"
    end

    @conflicting_instructions[xlen] << other_inst
  end
  @conflicting_instructions[xlen]
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.



402
# File 'lib/udb/obj/instruction.rb', line 402

def data_independent_timing? = @data["data_independent_timing"]

#decode_variables(base) ⇒ Array<DecodeVariable>

Returns The decode variables.

Returns:



1172
1173
1174
# File 'lib/udb/obj/instruction.rb', line 1172

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



406
407
408
# File 'lib/udb/obj/instruction.rb', line 406

def defined_in_base?(xlen)
  base.nil? || (base == xlen)
end

#encoding(base) ⇒ Object



1140
1141
1142
1143
1144
1145
1146
# File 'lib/udb/obj/instruction.rb', line 1140

def encoding(base)
  raise "#{name} is not defined in #{base}" unless defined_in_base?(base)

  load_encoding if @encodings.nil?

  @encodings[base]
end

#encoding_format(base) ⇒ Object

Raises:

  • (ArgumentError)


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/udb/obj/instruction.rb', line 225

def encoding_format(base)
  raise ArgumentError, "base must be 32 or 64" unless [32, 64].include?(base)

  if has_type?
    mask = "-" * type(base).length

    opcodes(base).each do |opcode|
      mask[type(base).length - opcode.range.end - 1, opcode.range.size] = opcode.value.to_s(2).rjust(T.must(opcode.range.size), "0")
    end

    mask
  else
    @encoding_format ||=
      if @data["encoding"].key?("RV32")
        {
          32 => @data["encoding"]["RV32"]["match"],
          64 => @data["encoding"]["RV64"]["match"]
        }
      else
        {
          32 => @data["encoding"]["match"],
          64 => @data["encoding"]["match"]
        }
      end
    @encoding_format[base]
  end
end

#encoding_widthObject



1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
# File 'lib/udb/obj/instruction.rb', line 1150

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

    encoding(64).size
  elsif defined_in_base?(32)
    encoding(32).size
  else
    raise "unexpected" unless defined_in_base?(64)

    encoding(64).size
  end

end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
128
# File 'lib/udb/obj/instruction.rb', line 124

def eql?(other)
  return nil unless other.is_a?(Instruction)

  @name.eql?(other.name)
end

#exists_in_cfg?(cfg_arch) ⇒ Boolean

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

Parameters:

Returns:

  • (Boolean)

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



1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
# File 'lib/udb/obj/instruction.rb', line 1216

def exists_in_cfg?(cfg_arch)
  if cfg_arch.fully_configured?
    (base.nil? || (cfg_arch.possible_xlens.include? base)) &&
      (defined_by_condition.satisfied_by_cfg_arch?(cfg_arch) == SatisfiedResult::Yes)
  else
    raise "unexpected cfg_arch type" unless cfg_arch.partially_configured?

    (base.nil? || (cfg_arch.possible_xlens.include? base)) &&
      (defined_by_condition.satisfied_by_cfg_arch?(cfg_arch) != SatisfiedResult::No)
  end
end

#fill_symtab(effective_xlen, ast) ⇒ Object



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/udb/obj/instruction.rb', line 415

def fill_symtab(effective_xlen, ast)
  symtab = cfg_arch.symtab.global_clone
  symtab.push(ast)
  symtab.add(
    "__instruction_encoding_size",
    Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: encoding_width.bit_length), encoding_width)
  )
  if effective_xlen.nil?
    if defined_in_base?(32)
      encoding(32).decode_variables.each do |d|
        qualifiers = [:const]
        qualifiers << :signed if d.sext?
        width = d.size

        var = Idl::Var.new(d.name, Idl::Type.new(:bits, qualifiers:, width:), decode_var: true)
        symtab.add(d.name, var)
      end
    end
    if defined_in_base?(64)
      encoding(64).decode_variables.each do |d|
        qualifiers = [:const]
        qualifiers << :signed if d.sext?
        width = d.size

        existing = symtab.get(d.name)
        if existing.nil?
          var = Idl::Var.new(d.name, Idl::Type.new(:bits, qualifiers:, width:), decode_var: true)
          symtab.add(d.name, var)
        else
          raise "An operand appears to be shadowing a global" if existing.type.kind != :bits
          # use the biggest
          if width > existing.type.width
            var = Idl::Var.new(d.name, Idl::Type.new(:bits, qualifiers:, width:), decode_var: true)
            symtab.add(d.name, var)
          end
        end
      end
    end

  else
    symtab.add(
      "__effective_xlen",
      Idl::Var.new("__effective_xlen", Idl::Type.new(:bits, width: 7), effective_xlen)
    )
    encoding(effective_xlen).decode_variables.each do |d|
      qualifiers = [:const]
      qualifiers << :signed if d.sext?
      width = d.size

      var = Idl::Var.new(d.name, Idl::Type.new(:bits, qualifiers:, width:), decode_var: true)
      symtab.add(d.name, var)
    end
  end

  symtab
end

#has_type?Boolean

Returns:

  • (Boolean)


131
# File 'lib/udb/obj/instruction.rb', line 131

def has_type? = @data.key?("format")

#hintsArray<Instruction>

Returns List of HINTs based on this instruction encoding.

Returns:

  • (Array<Instruction>)

    List of HINTs based on this instruction encoding



1210
1211
1212
# File 'lib/udb/obj/instruction.rb', line 1210

def hints
  @hints ||= @data.key?("hints") ? @data["hints"].map { |ref| @cfg_arch.ref(ref["$ref"]) } : []
end

#mask_to_array(int) ⇒ Object



527
528
529
530
531
532
533
534
535
536
537
538
# File 'lib/udb/obj/instruction.rb', line 527

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

#max_encoding_widthObject



1167
1168
1169
# File 'lib/udb/obj/instruction.rb', line 1167

def max_encoding_width
  [(rv32? ? encoding(32).size : 0), (rv64? ? encoding(64).size : 0)].max
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



1052
1053
1054
1055
1056
1057
1058
# File 'lib/udb/obj/instruction.rb', line 1052

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

#opcodes(base) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/udb/obj/instruction.rb', line 191

def opcodes(base)
  raise "Instruction #{name} is not defined in base RV#{base}" unless defined_in_base?(base)

  @opcodes ||= {}

  return @opcodes[base] unless @opcodes[base].nil?

  @opcodes[base] = @data["format"]["opcodes"].map do |opcode_name, opcode_data|
    next if opcode_name[0] == "$"

    raise "unexpected: opcode field is not contiguous" if opcode_data["location"].include?("|")

    loc = opcode_data["location"]
    range =
      if loc =~ /^([0-9]+)$/
        bit = ::Regexp.last_match(1)
        bit.to_i..bit.to_i
      elsif loc =~ /^([0-9]+)-([0-9]+)$/
        msb = ::Regexp.last_match(1)
        lsb = ::Regexp.last_match(2)
        raise "range must be specified 'msb-lsb'" unless msb.to_i >= lsb.to_i

        lsb.to_i..msb.to_i
      else
        raise "location format error"
      end
    Opcode.new(opcode_name, range, opcode_data["value"])
  end.reject(&:nil?)
end

#operation_astFunctionBodyAst

Returns The abstract syntax tree of the instruction operation.

Returns:

  • (FunctionBodyAst)

    The abstract syntax tree of the instruction operation



1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
# File 'lib/udb/obj/instruction.rb', line 1119

def operation_ast
  defer :operation_ast do
    return nil if @data["operation()"].nil?

    # now, parse the operation
    ast = cfg_arch.idl_compiler.compile_inst_operation(
      self,
      symtab: cfg_arch.symtab,
      input_file: @data["$source"],
      input_line: source_line(["operation()"])
    )

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

    ast
  end
end

#other_requirements(expand: false) ⇒ Object



1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
# File 'lib/udb/obj/instruction.rb', line 1268

def other_requirements(expand: false)
  # remove all the unconditional extension requirements
  cb = LogicNode.make_replace_cb do |node|
    next node unless node.type == LogicNodeType::Term
    rterm = node.children.fetch(0)
    next node unless rterm.is_a?(ExtensionTerm)

    # remove terms unconditionally true or false
    next LogicNode::True if unconditional_extension_requirements(expand: true).any? { |ext_req| ext_req.satisfied_by?(rterm.to_ext_req(@arch)) }
    # next LogicNode::False if unconditional_extension_conflicts(expand: true).any? { |ext_req| ext_req.satisfied_by?(rterm.to_ext_req(@arch)) }

    node
  end

  # remaining_requirements is the remainder of definedBy that is left if you remove unconditional
  # requirements
  remaining_requirements =
    defined_by_condition.to_logic_tree(expand:).replace_terms(cb).minimize(LogicNode::CanonicalizationType::SumOfProducts)

  t = remaining_requirements.type
  case t
  when LogicNodeType::True
    []
  when LogicNodeType::Or
    remaining_requirements.node_children.map { |child| LogicCondition.new(child, cfg_arch) }
  when LogicNodeType::And
    [LogicCondition.new(remaining_requirements.node_children.fetch(0), cfg_arch)]
  when LogicNodeType::Term, LogicNodeType::Not
    [LogicCondition.new(remaining_requirements, cfg_arch)]
  else
    raise "unexpected: #{t}"
  end
end

#processed_wavedrom_desc(base) ⇒ Object



253
254
255
256
257
# File 'lib/udb/obj/instruction.rb', line 253

def processed_wavedrom_desc(base)
  data = wavedrom_desc(base)
  processed_data = process_wavedrom(data)
  fix_entities(json_dump_with_hex_literals(processed_data))
end

#profiles_mandating_instObject



1304
1305
1306
1307
1308
1309
1310
1311
# File 'lib/udb/obj/instruction.rb', line 1304

def profiles_mandating_inst
  @profiles_mandating_inst ||=
    cfg_arch.profiles.select do |profile|
      profile.mandatory_ext_reqs.any? do |ext_req|
        defined_by_condition.satisfiability_depends_on_ext_req?(ext_req.ext_req)
      end
    end
end

#profiles_optioning_instObject



1315
1316
1317
1318
1319
1320
1321
1322
# File 'lib/udb/obj/instruction.rb', line 1315

def profiles_optioning_inst
  @profiles_optioning_inst ||=
    cfg_arch.profiles.select do |profile|
      profile.optional_ext_reqs.any? do |ext_req|
        defined_by_condition.satisfiability_depends_on_ext_req?(ext_req.ext_req)
      end
    end
end

#pruned_operation_ast(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



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/udb/obj/instruction.rb', line 474

def pruned_operation_ast(effective_xlen)
  @pruned_operation_ast ||= {}
  @pruned_operation_ast[effective_xlen] ||=
    begin
      if @data.key?("operation()")

        type_checked_ast = type_checked_operation_ast(effective_xlen)
        symtab = fill_symtab(effective_xlen, type_checked_ast)
        pruned_ast = type_checked_ast.prune(symtab)
        pruned_ast.freeze_tree(symtab)

        symtab.release
        pruned_ast
      end
    end
end

#reachable_exceptions(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()



514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/udb/obj/instruction.rb', line 514

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

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

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

Parameters:

  • 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()

Raises:

  • (ArgumentError)


542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/udb/obj/instruction.rb', line 542

def reachable_exceptions_str(effective_xlen = nil)
  raise ArgumentError, "effective_xlen is a #{effective_xlen.class} but must be an Integer or nil" unless effective_xlen.nil? || effective_xlen.is_a?(Integer)

  if @data["operation()"].nil?
    []
  else
    symtab = cfg_arch.symtab
    etype = symtab.get("ExceptionCode")
    if effective_xlen.nil?
      if cfg_arch.multi_xlen?
        if base.nil?
          (
            pruned_ast = pruned_operation_ast(32)
            symtab = fill_symtab(32, pruned_ast)
            e32 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
              etype.element_name(code)
            }
            symtab.release
            pruned_ast = pruned_operation_ast(64)
            symtab = fill_symtab(64, pruned_ast)
            e64 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
              etype.element_name(code)
            }
            symtab.release
            e32 + e64
          ).uniq
        else
          pruned_ast = pruned_operation_ast(base)
          symtab = fill_symtab(base, pruned_ast)
          e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
            etype.element_name(code)
          }
          symtab.release
          e
        end
      else
        effective_xlen = cfg_arch.mxlen
        pruned_ast = pruned_operation_ast(effective_xlen)
        symtab = fill_symtab(effective_xlen, pruned_ast)
        e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
          etype.element_name(code)
        }
        symtab.release
        e
      end
    else
      pruned_ast = pruned_operation_ast(effective_xlen)

      symtab = fill_symtab(effective_xlen, pruned_ast)
      e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
        etype.element_name(code)
      }
      symtab.release
      e
    end
  end
end

#reachable_functions(effective_xlen, cache = {}) ⇒ Object



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
# File 'lib/udb/obj/instruction.rb', line 495

def reachable_functions(effective_xlen, cache = {})
  if @data["operation()"].nil?
    []
  else
    @memo.reachable_functions ||= T.let({}, T::Hash[Integer, Idl::FunctionDefAst])
    @memo.reachable_functions[effective_xlen] ||=
      begin
        ast = operation_ast
        symtab = fill_symtab(effective_xlen, ast)
        fns = ast.reachable_functions(symtab, cache)
        symtab.release
        fns
      end
  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



1200
1201
1202
# File 'lib/udb/obj/instruction.rb', line 1200

def rv32?
  base != 64
end

#rv64?Boolean

Returns whether or not this instruction is defined for RV64.

Returns:

  • (Boolean)

    whether or not this instruction is defined for RV64



1205
1206
1207
# File 'lib/udb/obj/instruction.rb', line 1205

def rv64?
  base != 32
end

#subtype(base) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/udb/obj/instruction.rb', line 153

def subtype(base)
  @subtype ||= {
    32 =>
      if @data["format"].key?("RV32")
        @arch.ref(@data["format"]["RV32"]["subtype"]["$ref"])
      else
        @arch.ref(@data["format"]["subtype"]["$ref"])
      end,
    64 =>
      if @data["format"].key?("RV64")
        @arch.ref(@data["format"]["RV64"]["subtype"]["$ref"])
      else
        @arch.ref(@data["format"]["subtype"]["$ref"])
      end
  }
  @subtype[base]
end

#type(base) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/udb/obj/instruction.rb', line 134

def type(base)
  @type ||= {
    32 =>
      if @data["format"].key?("RV32")
        @arch.ref(@data["format"]["RV32"]["type"]["$ref"])
      else
        @arch.ref(@data["format"]["type"]["$ref"])
      end,
    64 =>
      if @data["format"].key?("RV64")
        @arch.ref(@data["format"]["RV64"]["type"]["$ref"])
      else
        @arch.ref(@data["format"]["type"]["$ref"])
      end
  }
  @type[base]
end

#type_checked_operation_ast(effective_xlen) ⇒ FunctionBodyAst

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

Parameters:

  • effective_xlen (Integer)

    32 or 64, the effective xlen to type check against

Returns:

  • (FunctionBodyAst)

    A type-checked abstract syntax tree of the operation



1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
# File 'lib/udb/obj/instruction.rb', line 1103

def type_checked_operation_ast(effective_xlen)
  defer :"type_checked_operation_ast_#{effective_xlen}" do
    return nil unless @data.key?("operation()")

    ast = operation_ast

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

    ast
  end
end

#unconditional_extension_conflicts(expand: false) ⇒ Object



1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/udb/obj/instruction.rb', line 1252

def unconditional_extension_conflicts(expand: false)
  ext_reqs = defined_by_condition.ext_req_terms(expand:)
  required_ext_reqs = ext_reqs.select do |ext_req|
    if defined_by_condition.mentions?(ext_req.extension)
      c = Condition.conjunction([defined_by_condition, ext_req.to_condition], cfg_arch)
      !c.satisfiable?
    end
  end

  required_ext_reqs.map(&:satisfying_versions).flatten.uniq.group_by { |ext_ver| ext_ver.name }.map do |ext_name, vers|
    ExtensionRequirement.create_from_ext_vers(vers)
  end
end

#unconditional_extension_requirements(expand: false) ⇒ Object



1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
# File 'lib/udb/obj/instruction.rb', line 1234

def unconditional_extension_requirements(expand: false)
  ext_reqs = defined_by_condition.ext_req_terms(expand:)
  required_ext_reqs = ext_reqs.select do |ext_req|
    if defined_by_condition.mentions?(ext_req.extension)
      c = Condition.conjunction([defined_by_condition, Condition.not(ext_req.to_condition, cfg_arch)], cfg_arch)
      !c.satisfiable?
    end
  end

  required_ext_reqs.map(&:satisfying_versions).flatten.uniq.group_by { |ext_ver| ext_ver.name }.map do |ext_name, vers|
    ExtensionRequirement.create_from_ext_vers(vers)
  end
end

#validate(resolver) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/udb/obj/instruction.rb', line 322

def validate(resolver)
  super(resolver)

  if has_type?
    if @data["format"]["RV32"].nil?
      b = base.nil? ? 64 : T.cast(base, Integer)
      Instruction.validate_encoding(self, b)
    else
      Instruction.validate_encoding(self, 32)
      Instruction.validate_encoding(self, 64)
    end
  else
    if @data["encoding"]["RV32"].nil?
      Instruction.deprecated_validate_encoding(@data["encoding"], name)
    else
      Instruction.deprecated_validate_encoding(@data["encoding"]["RV32"], name)
      Instruction.deprecated_validate_encoding(@data["encoding"]["RV64"], name)
    end
  end

  # Validate hint references
  if @data.key?("hints")
    @data["hints"].each_with_index do |hint, index|
      if hint.key?("$ref")
        begin
          # Try to dereference the hint to validate it exists
          hint_inst = @cfg_arch.ref(hint["$ref"])
          if hint_inst.nil?
            raise "Invalid hint reference in instruction '#{name}' at hints[#{index}]: '#{hint["$ref"]}' - reference not found"
          end
        rescue => e
          raise "Invalid hint reference in instruction '#{name}' at hints[#{index}]: '#{hint["$ref"]}' - #{e.message}"
        end
      end
    end
  end
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



1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
# File 'lib/udb/obj/instruction.rb', line 1185

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