evoasm 0.0.2.pre7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gemrelease +2 -0
  3. data/.gitignore +16 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.rake +8 -0
  6. data/Gemfile.rake.lock +51 -0
  7. data/LICENSE.txt +373 -0
  8. data/Makefile +6 -0
  9. data/README.md +43 -0
  10. data/Rakefile +128 -0
  11. data/bin/gdb +2 -0
  12. data/data/tables/README.md +19 -0
  13. data/data/tables/x64.csv +1684 -0
  14. data/data/templates/evoasm-x64.c.erb +319 -0
  15. data/data/templates/evoasm-x64.h.erb +126 -0
  16. data/evoasm.gemspec +30 -0
  17. data/examples/abs.yml +20 -0
  18. data/examples/popcnt.yml +17 -0
  19. data/examples/sym_reg.yml +26 -0
  20. data/exe/evoasm-search +13 -0
  21. data/ext/evoasm_ext/evoasm-alloc.c +145 -0
  22. data/ext/evoasm_ext/evoasm-alloc.h +59 -0
  23. data/ext/evoasm_ext/evoasm-arch.c +44 -0
  24. data/ext/evoasm_ext/evoasm-arch.h +161 -0
  25. data/ext/evoasm_ext/evoasm-bitmap.h +114 -0
  26. data/ext/evoasm_ext/evoasm-buf.c +130 -0
  27. data/ext/evoasm_ext/evoasm-buf.h +47 -0
  28. data/ext/evoasm_ext/evoasm-error.c +31 -0
  29. data/ext/evoasm_ext/evoasm-error.h +75 -0
  30. data/ext/evoasm_ext/evoasm-free-list.c.tmpl +121 -0
  31. data/ext/evoasm_ext/evoasm-free-list.h.tmpl +86 -0
  32. data/ext/evoasm_ext/evoasm-log.c +108 -0
  33. data/ext/evoasm_ext/evoasm-log.h +69 -0
  34. data/ext/evoasm_ext/evoasm-misc.c +23 -0
  35. data/ext/evoasm_ext/evoasm-misc.h +282 -0
  36. data/ext/evoasm_ext/evoasm-param.h +37 -0
  37. data/ext/evoasm_ext/evoasm-search.c +2145 -0
  38. data/ext/evoasm_ext/evoasm-search.h +214 -0
  39. data/ext/evoasm_ext/evoasm-util.h +40 -0
  40. data/ext/evoasm_ext/evoasm-x64.c +275624 -0
  41. data/ext/evoasm_ext/evoasm-x64.h +5436 -0
  42. data/ext/evoasm_ext/evoasm.c +7 -0
  43. data/ext/evoasm_ext/evoasm.h +23 -0
  44. data/ext/evoasm_ext/evoasm_ext.c +1757 -0
  45. data/ext/evoasm_ext/extconf.rb +31 -0
  46. data/lib/evoasm/cli/search.rb +127 -0
  47. data/lib/evoasm/cli.rb +6 -0
  48. data/lib/evoasm/core_ext/array.rb +9 -0
  49. data/lib/evoasm/core_ext/integer.rb +10 -0
  50. data/lib/evoasm/core_ext/kwstruct.rb +13 -0
  51. data/lib/evoasm/core_ext/range.rb +5 -0
  52. data/lib/evoasm/core_ext.rb +1 -0
  53. data/lib/evoasm/error.rb +20 -0
  54. data/lib/evoasm/examples.rb +27 -0
  55. data/lib/evoasm/gen/enum.rb +169 -0
  56. data/lib/evoasm/gen/name_util.rb +80 -0
  57. data/lib/evoasm/gen/state.rb +176 -0
  58. data/lib/evoasm/gen/state_dsl.rb +152 -0
  59. data/lib/evoasm/gen/strio.rb +27 -0
  60. data/lib/evoasm/gen/translator.rb +1102 -0
  61. data/lib/evoasm/gen/version.rb +5 -0
  62. data/lib/evoasm/gen/x64/funcs.rb +495 -0
  63. data/lib/evoasm/gen/x64/inst.rb +781 -0
  64. data/lib/evoasm/gen/x64.rb +237 -0
  65. data/lib/evoasm/gen.rb +8 -0
  66. data/lib/evoasm/program.rb +23 -0
  67. data/lib/evoasm/search.rb +40 -0
  68. data/lib/evoasm/tasks/gen_task.rb +86 -0
  69. data/lib/evoasm/tasks/template_task.rb +52 -0
  70. data/lib/evoasm/version.rb +3 -0
  71. data/lib/evoasm.rb +22 -0
  72. data/test/test_helper.rb +1 -0
  73. data/test/x64/test_helper.rb +19 -0
  74. data/test/x64/x64_test.rb +87 -0
  75. metadata +221 -0
@@ -0,0 +1,31 @@
1
+ require 'mkmf'
2
+
3
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
4
+
5
+ if have_header('capstone/capstone.h')
6
+ $LDFLAGS << ' -lcapstone'
7
+ end
8
+
9
+ $warnflags.gsub! '-Wdeclaration-after-statement', ''
10
+
11
+ $CFLAGS << ' -std=c11 -pedantic -fstrict-aliasing'
12
+ $warnflags << ' -Wextra -Wall -Wno-unused-label -Wuninitialized'\
13
+ ' -Wswitch-default -Wstrict-aliasing=3 -Wunreachable-code'\
14
+ ' -Wundef -Wpointer-arith -Wwrite-strings -Wconversion -Winit-self -Wno-unused-parameter'
15
+
16
+ $LDFLAGS << ''
17
+
18
+ if RbConfig::MAKEFILE_CONFIG['CC'] =~ /clang/
19
+ $warnflags << ' -Wno-unknown-warning-option -Wno-parentheses-equality -Wno-error=ignored-attributes'\
20
+ ' -Wno-missing-field-initializers -Wno-missing-braces'
21
+ end
22
+
23
+ if enable_config('debug')
24
+ $warnflags << ' -Werror -Wno-error=unused-function -Wno-error=pedantic'\
25
+ ' -Wno-error=implicit-function-declaration'
26
+ $defs.push('-DEVOASM_MIN_LOG_LEVEL=EVOASM_LOG_LEVEL_DEBUG')
27
+ $CFLAGS.gsub!(/-O\d/, '')
28
+ $CFLAGS << ' -O0 -g3 -fno-omit-frame-pointer'
29
+ end
30
+
31
+ create_makefile('evoasm_ext')
@@ -0,0 +1,127 @@
1
+ require 'yaml'
2
+ require 'pastel'
3
+ require 'pry'
4
+
5
+ module Evoasm
6
+ module Cli
7
+ class Search
8
+ attr_reader :filename
9
+
10
+ def initialize(filename, options)
11
+ @filename = filename
12
+ raise ArgumentError, 'filename is nil' if filename.nil?
13
+
14
+ if options.any?{|o| o =~ /\--log-level=(\d)/}
15
+ Evoasm.log_level = $1.to_i
16
+ end
17
+ end
18
+
19
+ def start!
20
+ x64 = X64.new
21
+ params = YAML.load(File.read filename)
22
+ insts = filter_insts x64.instructions, params['instructions']
23
+
24
+ p insts.map(&:name)
25
+ pastel = Pastel.new
26
+
27
+ program_size = parse_range params['program_size']
28
+ kernel_size = parse_range params['kernel_size']
29
+ program_counter = 0
30
+ max_programs = params['max_programs']
31
+ recur_limit = params['recur_limit'] || 0
32
+
33
+ domains = convert_domains_hash params['domains']
34
+ parameters = (params['parameters'] || %i(reg0 reg1 reg2 imm0 imm1)).map(&:to_sym)
35
+
36
+ start_ts = Time.now
37
+
38
+ search = Evoasm::Search.new x64,
39
+ examples: params['examples'],
40
+ instructions: insts,
41
+ kernel_size: kernel_size,
42
+ program_size: program_size,
43
+ population_size: params['population_size'],
44
+ parameters: parameters,
45
+ domains: domains,
46
+ recur_limit: recur_limit
47
+
48
+ search.start!(params['max_loss'] || 0.0) do |program, loss|
49
+ ts = Time.now
50
+ puts pastel.bold "Program #{program_counter}, #{ts.strftime '%H:%M:%S'} (found after #{(ts - start_ts).to_i} seconds)"
51
+
52
+ if program.buffer.respond_to? :disassemble
53
+ puts program.buffer.disassemble.join "\n"
54
+ else
55
+ puts program.instructions.map(&:name)
56
+ end
57
+
58
+ puts
59
+
60
+ if params['console'] != false
61
+ binding.pry
62
+ end
63
+
64
+ program_counter += 1
65
+
66
+ if program_counter == max_programs
67
+ # stops search
68
+ return false
69
+ end
70
+ end
71
+ end
72
+
73
+ private
74
+ def filter_insts(insts, params)
75
+ op_types = %i(rm reg imm)
76
+ reg_types = %i(rflags)
77
+ bad_regs = %i(SP IP)
78
+
79
+ grep_regexp = params && Regexp.new(params['grep']) rescue nil
80
+ grep_v_regexp = params && Regexp.new(params['grep_v']) rescue nil
81
+
82
+ reg_types.concat params['reg_types'].map(&:to_sym)
83
+
84
+ insts.select do |inst|
85
+ next false if grep_regexp && inst.name !~ grep_regexp
86
+ next false if grep_v_regexp && inst.name =~ grep_v_regexp
87
+ next false if inst.operands.size == 0
88
+
89
+ inst.operands.all? do |op|
90
+ next false unless op_types.include? op.type
91
+ if op.register
92
+ next false unless reg_types.include?(op.register.type)
93
+ next false if bad_regs.include? op.register.name
94
+ end
95
+
96
+ true
97
+ end
98
+ end
99
+ end
100
+
101
+ def convert_domains_hash(hash)
102
+ Hash(hash).map do |k, v|
103
+ new_k = k.to_sym
104
+ new_v =
105
+ case v
106
+ when Array
107
+ v.map {|e| e.is_a?(String) ? e.to_sym : e }
108
+ else
109
+ v
110
+ end
111
+ [new_k, new_v]
112
+ end.to_h
113
+ end
114
+
115
+ def parse_range(str)
116
+ case str
117
+ when Integer
118
+ str
119
+ when /^\(?(\d+)\.\.(\.?)(\d+)\)?$/
120
+ Range.new($1.to_i, $3.to_i, !$2.empty?)
121
+ else
122
+ raise ArgumentError, "invalid range '#{str}'"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
data/lib/evoasm/cli.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Evoasm
2
+ module Cli
3
+ end
4
+ end
5
+
6
+ require 'evoasm/cli/search'
@@ -0,0 +1,9 @@
1
+ class Array
2
+ def keys
3
+ map { |k, _v| k }
4
+ end
5
+
6
+ def values
7
+ map { |_k, v| v }
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class Integer
2
+ INT8_MAX = 0x7f
3
+ INT8_MIN = -INT8_MAX - 1
4
+ INT16_MAX = 0x7fff
5
+ INT16_MIN = -INT16_MAX - 1
6
+ INT32_MAX = 0x7fffffff
7
+ INT32_MIN = -INT32_MAX - 1
8
+ INT64_MAX = 0x7fffffffffffffff
9
+ INT64_MIN = -INT64_MAX - 1
10
+ end
@@ -0,0 +1,13 @@
1
+ # The MIT License (MIT)
2
+ # Copyright (c) 2015 Maxim Chernyak
3
+ class KwStruct < Struct
4
+ def self.new(*members, &block)
5
+ super.tap do |struct_class|
6
+ struct_class.class_eval <<-RUBY
7
+ def initialize(#{members.map { |m| "#{m}: nil" }.join(', ')})
8
+ super(#{members.join(', ')})
9
+ end
10
+ RUBY
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class Range
2
+ def sample
3
+ rand self
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'evoasm/core_ext/range'
@@ -0,0 +1,20 @@
1
+ module Evoasm
2
+ class Error
3
+ def message
4
+ msg = __message
5
+
6
+ case code
7
+ when :not_encodable
8
+ "#{msg} #{parameter}"
9
+ when :missing_param
10
+ "#{msg} (#{parameter})"
11
+ when :missing_feature
12
+ "missing features #{features.join ', '}"
13
+ when :invalid_access
14
+ "#{msg} (#{instruction}/#{register})"
15
+ else
16
+ msg || code
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module Evoasm
2
+ module Examples
3
+
4
+ class << self
5
+ def convert(examples)
6
+ examples.inject(nil) do |(in_arity, out_arity), example|
7
+ inputs, outputs = example
8
+ example_in_arity = inputs.size
9
+ example_out_arity = outputs.size
10
+
11
+ validate_example inputs, example_in_arity, in_arity
12
+ validate_example outputs, example_out_arity, out_arity
13
+
14
+ [example_in_arity, example_out_arity]
15
+ end.unshift(examples.flatten)
16
+ end
17
+
18
+ private
19
+ def validate_example(example, example_arity, arity)
20
+ if arity && arity != example_arity
21
+ raise ArgumentError, "invalid arity for example '#{example}'"\
22
+ " (#{example_arity} for #{arity})"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,169 @@
1
+ require 'evoasm/gen/strio'
2
+ require 'evoasm/gen/name_util'
3
+
4
+ module Evoasm
5
+ module Gen
6
+ class Enum
7
+ include NameUtil
8
+
9
+ attr_reader :name, :flags
10
+ alias_method :flags?, :flags
11
+
12
+ def initialize(name = nil, elems = [], prefix: nil, flags: false)
13
+ @name = name
14
+ @prefix = prefix
15
+ @map = {}
16
+ @counter = 0
17
+ @flags = flags
18
+ add_all elems
19
+ end
20
+
21
+ def n
22
+ @counter
23
+ end
24
+
25
+ def to_c(io = StrIO.new, typedef: true)
26
+ raise 'name missing' if !name
27
+
28
+ type_name = c_type_name
29
+
30
+ io.puts "#{typedef ? 'typedef ' : ''}enum #{type_name} {"
31
+ io.indent do
32
+ each do |elem, value|
33
+ elem_name = elem_name_to_c elem
34
+ c_value =
35
+ if valid_elem?(value)
36
+ elem_name_to_c value
37
+ else
38
+ if flags?
39
+ "1 << #{value}"
40
+ else
41
+ "#{value}"
42
+ end
43
+ end
44
+ io.puts "#{elem_name} = #{c_value},"
45
+ end
46
+ if !flags?
47
+ io.puts n_elem_to_c
48
+ end
49
+ end
50
+ io.write '}'
51
+ io.write " #{type_name}" if typedef
52
+ io.puts ';'
53
+ io.puts "#define #{bitsize_to_c} #{bitsize}"
54
+ unless flags?
55
+ io.puts "#define #{bitsize_to_c true} #{bitsize true}"
56
+ else
57
+ io.puts "#define #{all_to_c} #{all_value}"
58
+ end
59
+
60
+ io.string
61
+ end
62
+
63
+ def bitsize(with_n = false)
64
+ if flags?
65
+ @map.size
66
+ else
67
+ Math.log2(max + 1 + (with_n ? 1 : 0)).ceil.to_i
68
+ end
69
+ end
70
+
71
+ def max
72
+ @map.each_with_index.inject(0) do |acc, (index, (k, v))|
73
+ if v
74
+ [v + 1, acc + 1].max
75
+ else
76
+ acc + 1
77
+ end
78
+ end - 1
79
+ end
80
+
81
+ def c_type(typedef = false)
82
+ "#{typedef ? '' : 'enum '}#{c_type_name}"
83
+ end
84
+
85
+ def c_type_name
86
+ name_to_c name, @prefix
87
+ end
88
+
89
+ def all_to_c
90
+ name_to_c "#{prefix_name}_all", @prefix, const: true
91
+ end
92
+
93
+ def all_value
94
+ (2**@map.size) - 1
95
+ end
96
+
97
+ def bitsize_to_c(with_n = false)
98
+ name_to_c "#{prefix_name}_bitsize#{with_n ? '_WITH_N' : ''}", @prefix, const: true
99
+ end
100
+
101
+ def n_elem_to_c
102
+ name_to_c "n_#{prefix_name}s", @prefix, const: true
103
+ end
104
+
105
+ def keys
106
+ @map.keys
107
+ end
108
+
109
+ def add(elem, alias_elem = nil)
110
+ fail ArgumentError, 'can only add symbols or strings' \
111
+ unless valid_elem?(elem) && (!alias_elem || valid_elem?(alias_elem))
112
+
113
+ return if @map.key? elem
114
+
115
+ value = alias_elem || @counter
116
+ @counter += 1 if alias_elem.nil?
117
+
118
+ @map[elem] = value
119
+ end
120
+
121
+ def add_all(elems)
122
+ elems.each do |elem|
123
+ add elem
124
+ end
125
+ end
126
+
127
+ def each(&block)
128
+ return to_enum(:each) if block.nil?
129
+ @map.each_key do |k|
130
+ block[k, self[k]]
131
+ end
132
+ end
133
+
134
+ def alias(key)
135
+ key = @map[key]
136
+ case key
137
+ when Symbol, String
138
+ key
139
+ else
140
+ nil
141
+ end
142
+ end
143
+
144
+ def [](elem)
145
+ value = @map[elem]
146
+
147
+ if @map.key? value
148
+ @map.fetch value
149
+ else
150
+ value
151
+ end
152
+ end
153
+
154
+ private
155
+ def prefix_name
156
+ name.to_s.sub(/_id$/, '')
157
+ end
158
+
159
+ def elem_name_to_c(elem_name)
160
+ # convention: _id does not appear in element's name
161
+ name_to_c elem_name, Array(@prefix) + [prefix_name], const: true
162
+ end
163
+
164
+ def valid_elem?(elem)
165
+ elem.is_a?(Symbol) || elem.is_a?(String)
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,80 @@
1
+ module Evoasm
2
+ module Gen
3
+ module NameUtil
4
+ def namespace
5
+ 'evoasm'
6
+ end
7
+
8
+ def const_name_to_c(name, prefix)
9
+ name_to_c name, prefix, const: true
10
+ end
11
+
12
+ def name_to_c(name, prefix = nil, const: false)
13
+ c_name = [namespace, *prefix, name.to_s.sub(/\?$/, '')].compact.join '_'
14
+ if const
15
+ c_name.upcase
16
+ else
17
+ c_name
18
+ end
19
+ end
20
+
21
+ def indep_arch_prefix(name = nil)
22
+ ['arch', name]
23
+ end
24
+
25
+ def arch_prefix(name = nil)
26
+ [arch, name]
27
+ end
28
+
29
+ def error_code_to_c(name)
30
+ prefix = name == :ok ? :error_code : indep_arch_prefix(:error_code)
31
+ const_name_to_c name, prefix
32
+ end
33
+
34
+ def reg_name_to_c(name)
35
+ const_name_to_c name, arch_prefix(:reg)
36
+ end
37
+
38
+ def exception_to_c(name)
39
+ const_name_to_c name, arch_prefix(:exception)
40
+ end
41
+
42
+ def reg_type_to_c(name)
43
+ const_name_to_c name, arch_prefix(:reg_type)
44
+ end
45
+
46
+ def operand_type_to_c(name)
47
+ const_name_to_c name, arch_prefix(:operand_type)
48
+ end
49
+
50
+ def inst_name_to_c(inst)
51
+ const_name_to_c inst.name, arch_prefix(:inst)
52
+ end
53
+
54
+ def operand_size_to_c(size)
55
+ const_name_to_c size, :operand_size
56
+ end
57
+
58
+ def bit_mask_to_c(mask)
59
+ name =
60
+ case mask
61
+ when Range then"#{mask.min}_#{mask.max}"
62
+ else mask.to_s
63
+ end
64
+ const_name_to_c name, arch_prefix(:bit_mask)
65
+ end
66
+
67
+ def feature_name_to_c(name)
68
+ const_name_to_c name, arch_prefix(:feature)
69
+ end
70
+
71
+ def inst_flag_to_c(flag)
72
+ const_name_to_c flag, arch_prefix(:inst_flag)
73
+ end
74
+
75
+ def param_name_to_c(name)
76
+ const_name_to_c name, arch_prefix(:param)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,176 @@
1
+ require 'set'
2
+
3
+ module Evoasm::Gen
4
+ State = Struct.new(:children, :actions, :ret, :_local_params) do
5
+ attr_accessor :id, :comment, :parents
6
+
7
+ def initialize
8
+ self.children = []
9
+ self.parents = []
10
+ self.actions = []
11
+ self._local_params = []
12
+ end
13
+
14
+ def local_params
15
+ child_local_params = children.map do |child, _, _|
16
+ child.local_params
17
+ end
18
+ all_local_params = (_local_params + child_local_params)
19
+ all_local_params.flatten!
20
+ all_local_params.uniq!
21
+
22
+ all_local_params
23
+ end
24
+
25
+ def add_local_param(param)
26
+ if param.to_s[0] != '_'
27
+ fail ArgumentError, 'params must start with underscore'
28
+ end
29
+
30
+ _local_params << param unless _local_params.include? param
31
+ end
32
+
33
+ protected def add_parent(parent)
34
+ parents << parent unless parents.include? parent
35
+ end
36
+
37
+ def add_child(child, cond = nil, priority)
38
+ child.add_parent self
39
+ children << [child, cond, priority]
40
+ end
41
+
42
+ %i(sets asserts calls writes debugs).each do |name|
43
+ action_name = name.to_s[0..-2].to_sym
44
+ define_method name do
45
+ actions.select { |action, _| action == action_name }
46
+ .map { |_, args| args }
47
+ end
48
+ end
49
+
50
+ private def roots
51
+ return [self] if parents.empty?
52
+ parents.flat_map(&:roots)
53
+ end
54
+
55
+ def root
56
+ roots = roots()
57
+ fail 'multiple roots' if roots.size > 1
58
+ roots.first
59
+ end
60
+
61
+ def empty?
62
+ actions.empty?
63
+ end
64
+
65
+ def terminal?
66
+ children.empty?
67
+ end
68
+
69
+ def ret?
70
+ ret != nil
71
+ end
72
+
73
+ def to_gv
74
+ require 'gv'
75
+
76
+ graph = GV::Graph.open 'ast'
77
+ graph[:ranksep] = 1.5
78
+ graph[:statesep] = 0.8
79
+ __to_gv__ graph
80
+ graph
81
+ end
82
+
83
+ def __to_gv__(graph, gv_parent = nil, cond = nil, attrs = {}, index = nil, seen = {})
84
+ if seen.key?(self)
85
+ # return
86
+ else
87
+ seen[self] = true
88
+ end
89
+
90
+ edge_label = ''
91
+ state_label = ''
92
+
93
+ if cond
94
+ if cond.first == :else
95
+ edge_label << "<b> else</b><br></br>\n"
96
+ else
97
+ edge_label << "<b> if</b> #{expr_to_s cond}<br></br>\n"
98
+ end
99
+ end
100
+
101
+ if attrs
102
+ attrs.each do |name, value|
103
+ edge_label << "<b> #{name}</b>: #{value}<br></br>\n"
104
+ end
105
+ end
106
+
107
+ actions.each do |name, args|
108
+ state_label << send(:"label_#{name}", *args)
109
+ end
110
+
111
+ state_label << "<i>#{comment}</i>\n" if comment
112
+
113
+ gv_state = graph.node object_id.to_s,
114
+ shape: (self.ret? ? :house : (state_label.empty? ? :point : :box)),
115
+ label: graph.html(state_label)
116
+
117
+ children.each_with_index do |(child, cond, attrs), index|
118
+ child.__to_gv__(graph, gv_state, cond, attrs, index, seen)
119
+ end
120
+
121
+ if gv_parent
122
+ graph.edge gv_parent.name + '.' + gv_state.name + index.to_s,
123
+ gv_parent, gv_state,
124
+ label: graph.html(edge_label)
125
+ end
126
+
127
+ graph
128
+ end
129
+
130
+ private
131
+
132
+ def label_set(name, value, _options = {})
133
+ "<b>set</b> #{name} := #{expr_to_s value}<br></br>"
134
+ end
135
+
136
+ def label_assert(cond)
137
+ "<b>assert</b> #{expr_to_s cond}<br></br>"
138
+ end
139
+
140
+ def label_call(name)
141
+ "<b>call</b> #{name}<br></br>"
142
+ end
143
+
144
+ def label_debug(_format, *_args)
145
+ ''
146
+ end
147
+
148
+ def label_write(value, size)
149
+ label =
150
+ if value.is_a?(Integer) && size.is_a?(Integer)
151
+ if size == 8
152
+ 'x%x' % value
153
+ else
154
+ "b%0#{size}b" % value
155
+ end
156
+ elsif size.is_a? Array
157
+ Array(value).zip(Array(size)).map do |v, s|
158
+ "#{expr_to_s v} [#{expr_to_s s}]"
159
+ end.join ', '
160
+ else
161
+ "#{expr_to_s value} [#{expr_to_s size}]"
162
+ end
163
+ "<b>output</b> #{label}<br></br>"
164
+ end
165
+
166
+ def expr_to_s(pred)
167
+ case pred
168
+ when Array
169
+ pred, *args = *pred
170
+ "#{pred}(#{args.map { |a| expr_to_s(a) }.join(', ')})"
171
+ else
172
+ pred
173
+ end
174
+ end
175
+ end
176
+ end