new world
[rubydium.git] / baby / native.rb
blob8b339e2eaa90951f0bbc3060ded84e312df33fb1
1 require 'singleton'
2 require 'delegate'
3 require 'src/bench' if !(defined? $labels)
4 $caller_on = false
5 $debug_on  = false
6 $code_on   = false
7 def dump_caller caller_array
8 puts caller_array.reject{|line| line =~ /\/test\/unit/}.reverse.join("\n").indent 3
9 caller_array.first =~ /(.*):(.*?):in/
10 fname, line_num = $1, $2.to_i
11 File.open(fname, "r") {
12    |file|
13    num_to_get = [line_num - 4, line_num].min
14    num_to_get.times { file.gets }
15    7.times {
16       print ((file.lineno+1) == line_num ? "[+] " : "    ")
17       puts file.gets
18    }
20 end
21 class Ref
22    attr_accessor :data 
23    def initialize data
24       @data = data
25    end
26 end
27 class IdentGen
28    include Singleton
29    attr_accessor :top_ident
30    def initialize
31       @top_ident = 0
32    end
33    def get_ident
34       tmp = @top_ident
35       @top_ident += 1
36       tmp
37    end
38 end
39 class Memory
40    attr_reader :ranges, :hits, :misses
41    def initialize
42       @top_addr = 2**8
43       @ranges = {}
44       @hits, @misses = 0, 0
45       @previous_rng = nil
46    end
47    def report
48       print "Memory report: #{@misses}/#{@hits} (m/h)"
49    end
50    def format_ranges
51       @ranges.map{|rng|rng.inspect}.join("\n").indent 3
52    end
53    def yield_correct_range addr
54       if !@previous_rng.nil? and @previous_rng === addr
55          @hits += 1
56          yield @previous_rng, @ranges[@previous_rng]
57          return
58       else 
59          @misses += 1
60       end
61       @ranges.each_pair {
62          |rng, data|
63          next unless rng === addr
64          @previous_rng = rng
65          yield rng, data
66          break
67       }
68    end
69    def load_elem addr, offs
70       found, value = false, nil
71    bench("runtime") {
72       yield_correct_range(addr) {
73          |rng, data|
74          value = data[offs]
75          raise "loaded a nil element!" if value.nil?
76          puts "LOADED #{value} AT #{addr} + #{offs}\nFROM\n#{format_ranges}" if $debug_on
77          found = true
78       }
79       raise "can't find ptr!!!! - #{addr} + #{offs}" unless found
80    }
81       value
82    end
83    def store_elem addr, offs, value
84       found = false
85    bench("runtime") {
86       yield_correct_range(addr) {
87          |rng, data|
88          puts "STORING #{value} AT #{addr} + #{offs}\nIN\n#{format_ranges}, WAS == #{data[offs]}" if $debug_on
89          data[offs] = value
90          found = true
91       }
92    }
93       raise "can't find ptr!!!! - #{addr} + #{offs}" unless found
94    end
95    def ptr2string ptr, size
96       found, value = nil, nil
97    bench("runtime") {
98       yield_correct_range(ptr) {
99          |rng, data|
100          subset = data[(ptr - rng.first) / 4, size / 4] || [] # range with zero size -> nil || []
101          ((size / 4) - subset.size).times { subset << nil } if subset.size != (size / 4)
102          raise "ARGH! - #{subset.size} != #{(size / 4)}" if subset.size != (size / 4)
103          puts "LOADING #{subset.inspect} AT #{ptr}..#{ptr + size}\nFROM\n#{format_ranges}" if $debug_on
104          found = true
105          value = subset.map { |int| (int.is_a? Function) ? -666 : int }.pack("i*")
106       }
107    }
108       raise "can't find ptr!!!! - #{ptr} .. #{ptr + size}" unless found
109       value
110    end
111    def string2ptr str
112       bytes = [str.length, 2]
113       str.each_byte { |b| bytes += [b, 2] }
114       allocate str.length + 1, bytes
115    end
116    def allocate size, object
117       raise "argh, out of memory!" if @top_addr > 2**32
118       # raise "eek! size isn't a multiple of 4!" if (size % 4) != 0
119       address = @top_addr
120       @ranges[address...(@top_addr + (size*4))] = object
121       @top_addr += (size*4) + 2**10
122       return address
123    end
125 class Context
126    attr_accessor :data_inspector, :builder_function, :my_callback
127    attr_reader :memory
128    def initialize
129       @memory = Memory.new
130       $memory = @memory
131    end
133 class Value
134    attr_accessor :ident, :type, :size, :ref
135    def initialize
136       @ident = IdentGen::instance.get_ident
137       @ref   = Ref.new nil
138    end
139    def value
140       @ref.data
141    end
142    def value= a
143       @ref.data = a
144    end
145    def self.ptr2string ptr, size
146       $memory.ptr2string ptr, size
147    end
148    def self.string2ptr str
149       $memory.string2ptr str
150    end
152 class Label
153    attr_accessor :ident, :pos
154    def initialize
155       @ident = IdentGen::instance.get_ident
156    end
158 Prototype = Struct.new :ret_type, :param_types
159 Instruction = Struct.new :caller, :insn, :params
160 class Instruction 
161    def inspect
162       Struct.new(:insn, :params).new(insn, params.map { |param| ((param.ref.data.is_a? Function) rescue false) ? "<Function>" : param }).inspect
163    end
164    def to_s
165       return "<too much output>" if insn == :insn_call_indirect_vtable_blah
166       indent = " " * 3
167       "Instruction :: #{insn}" + ([nil] + params).map{|param|param.nil? ? "" : param.inspect}.join("\n#{indent}")
168    end
170 class Function
171    attr_accessor :context
172    def initialize context
173       @code = []
174       @context = context
175       @prototype = nil
176       $times = Hash.new { 0 }
177    end
178    def compile
179       # dummy
180    end
181    def lock
182       # todo - lock
183       yield
184    end
185    def fill_value_with_constant val, type, value
186       val.type, val.value = type, value
187    end
188    def fill_value_with_param val, idx
189       val.ref = @params[idx].ref
190    end
191    def create_with_prototype ret_type, param_types
192       @prototype = Prototype.new ret_type, param_types
193       @params = param_types.map {
194          |type|
195          param = Value.new
196          param.type = type
197          param.ref = Ref.new -1
198          param
199       }
200    end
201    def method_missing name, *params
202       raise "u gotta call create_with_prototype!" if @prototype.nil?
203       vars = params
204       case name
205       when :insn_label
206          label = *params
207          label.pos = @code.length
208       end
209       @code << Instruction.new($caller_on ? caller : nil, name, params)
210    end
211    def create_local value, type
212       value.value = nil
213       value.type = type
214    end
215    def apply params
216       dispatch params
217    end
218    def dispatch params
219       func = self
220       loop {
221          func, params = func.exec params
222          return params if func.nil?
223       }
224    end
225    def exec params # note - param unused, maybe abstract in layer above, this is the wrong layer!
226       result = nil
227    bench("exec") {
228       catch(:done) {
229          params.each_with_index {
230             |value, idx|
231             @params[idx].ref.data = value
232          }
233          position = 0
234          if $code_on
235             puts @code.map {|instr| instr.to_s }.join("\n").indent 3
236          end
237          hash_math = {  
238             :insn_add => :+,
239             :insn_sub => :-,
240             :insn_mul => :*,
241             :insn_div => :/,
242             :insn_rem => :%
243          } 
244          hash_comp = {
245             :insn_eq => :==,
246             :insn_ne => :==, # see below
247             :insn_lt => :<,
248             :insn_gt => :>
249          }
250          math_operators = hash_math.keys + hash_comp.keys
251          loop {
252             begin
253                new_position = position + 1
254                instr = @code[position]
255                break if instr.nil?
256                time = Time.new
257                case instr.insn
258                when *math_operators
259                   ret, src1, src2 = *instr.params
260                   bool_result = hash_comp.has_key? instr.insn
261                   value = src1.value.send((hash_math.merge hash_comp)[instr.insn], src2.value)
262                   ret.value = bool_result ? (value ? 1 : 0) : value
263                   ret.value = (1 - ret.value) if instr.insn == :insn_ne
264                when :insn_branch_if, :insn_branch_if_not
265                   cond, label = *instr.params
266                   expected_value = (instr.insn == :insn_branch_if_not) ? 0 : 1
267                   new_position = label.pos if cond.value == expected_value
268                when :insn_branch
269                   label = *instr.params
270                   new_position = label.pos
271                when :insn_return
272                   val = *instr.params
273                   result = [nil, val.value]
274                   throw :done
275                when :insn_store
276                   dst, src = *instr.params
277                   dst.value = src.value
278                when :insn_call_print_int
279                   val = *instr.params
280                   if @context.my_callback.nil?
281                   puts val.value
282                   else
283                      @context.my_callback.call val.value
284                   end
285                when :insn_call_alloc_bytearray
286                   addr, size = *instr.params
287                   addr.size  = size.value
288                   addr.value = @context.memory.allocate(size.value, [])
289                when :insn_call_data_inspect
290                   p1, p2, p3, p4 = *instr.params
291                   @context.data_inspector.call p1.value, p2.value, p3.value, p4.value
292                when :insn_call_build_function
293                   ret, p2, p3, p4 = *instr.params
294                   bench("builder_function") {
295                      ret.value = @context.builder_function.call p2.value, p3.value, p4.value
296                   }
297                when :insn_call_indirect_vtable_blah
298                   time = nil
299                   ret, func, ret_type, func_prototype, params = *instr.params
300                   result = [func.value, params.map{ |param| param.value }]
301                   throw :done
302                when :insn_store_elem
303                   puts "BEFORE store: - #{$memory.ranges.inspect}" if $debug_on
304                   addr, idx, src = *instr.params
305                   raise "out of bounds! - #{idx.value}" if !addr.size.nil? and idx.value > addr.size
306                   $memory.store_elem(addr.value, idx.value, src.value)
307                   puts "AFTER store:  - #{$memory.ranges.inspect}" if $debug_on
308                when :insn_load_elem
309                   puts "DURING load: - #{$memory.ranges.inspect}" if $debug_on
310                   dst, addr, idx, type = *instr.params
311                   raise "out of bounds! - #{idx.value}" if !addr.size.nil? and idx.value > addr.size
312                   dst.type  = type
313                   dst.value = $memory.load_elem(addr.value, idx.value)
314                when :insn_label
315                   #
316                else
317                   raise "INSTRUCTION #{instr.inspect} NOT HANDLED"
318                end
319                new_time = Time.new
320                $times[instr.insn] += (new_time - time).to_f unless time.nil?
321                position = new_position
322                if $debug_on
323                   dump_caller instr.caller if $caller_on
324                   puts "Post-instruction State ::"
325                   puts instr.to_s
326                   puts
327                end
328             rescue => e
329                if $caller_on
330                   dump_caller instr.caller
331                else
332                   puts "SWITCH ON DEBUGGING! if you want to know more!"
333                   p e
334                end
335                puts e.backtrace.join("\n").indent 3
336                puts "Current instruction:"
337                puts instr.to_s
338                puts "Exception: #{e.inspect}"
339                exit
340             end
341          }
342       }
343    }
344       return *result
345    end
348 def mk_constant func, constant_type, integer
349    return_value = Value.new
350    func.fill_value_with_constant return_value, constant_type, integer
351    return_value 
354 def get_param func, param_idx
355    val = Value.new
356    func.fill_value_with_param val, param_idx
357    val
360 require "test/unit"
362 class LoggingContext < Context
363   attr_reader :log
364   def initialize
365     super
366     @log = []
367     self.my_callback = proc { |b| @log << b }
368   end
371 class Test_All < Test::Unit::TestCase
372   def test_7
373      context = Context.new
374      func = Function.new context
375      func.create_with_prototype :int, [:int, :int]
376      func.lock {
377         ret = Value.new
378         val1 = get_param func, 0
379         val2 = get_param func, 1
380         func.insn_add ret, val1, val2
381         func.insn_return ret
382         func.compile
383      }
384      assert_equal 7, (func.apply [2, 5])
385   end
387   def test_6
388      context = Context.new
389      context.builder_function = proc {
390         |p2, p3, dummy|
391         s_context = Context.new
392         s_func = Function.new s_context
393         s_func.create_with_prototype :int, []
394         s_func.lock {
395            s_ret, s_val1, s_val2 = Value.new, Value.new, Value.new
396            s_func.fill_value_with_constant s_val1, :int, p2
397            s_func.fill_value_with_constant s_val2, :int, p3
398            s_func.insn_add s_ret, s_val1, s_val2
399            s_func.insn_return s_ret
400            s_func.compile
401         }
402         s_func
403      }
404      func = Function.new context
405      func.create_with_prototype :int, []
406      func.lock {
407         mem_addr = Value.new
408         func.insn_call_alloc_bytearray mem_addr, mk_constant(func, :int, 2)
409         func.insn_store_elem mem_addr, mk_constant(func, :int, 0), mk_constant(func, :int, 32)
410         func.insn_store_elem mem_addr, mk_constant(func, :int, 1), mk_constant(func, :int, 64)
411         func_ptr = Value.new
412         func.insn_call_build_function func_ptr, mk_constant(func, :int, 2), mk_constant(func, :int, 3), mk_constant(func, :int, -1)
413         ret_value = Value.new
414         func.insn_call_indirect_vtable_blah ret_value, func_ptr, nil, nil, []
415         func.insn_return ret_value
416         func.compile
417      }
418      assert_equal 5, (func.apply [])
419   end
421   def test_5
422      context = Context.new
423      context.builder_function = proc {
424         |p2, p3, p4|
425         p2 + p3 + p4
426      }
427      func = Function.new context
428      func.create_with_prototype :int, []
429      func.lock {
430         mem_addr = Value.new
431         func.insn_call_alloc_bytearray mem_addr, mk_constant(func, :int, 2)
432         func.insn_store_elem mem_addr, mk_constant(func, :int, 0), mk_constant(func, :int, 32)
433         func.insn_store_elem mem_addr, mk_constant(func, :int, 1), mk_constant(func, :int, 64)
434         func_ptr = Value.new
435         func.insn_call_build_function func_ptr, mk_constant(func, :int, 0), mk_constant(func, :int, 2), mk_constant(func, :int, 3)
436         func.insn_return func_ptr 
437         func.compile
438      }
439      assert_equal 5, (func.apply [])
440   end
442   def test_4
443      context = Context.new
444      inspect_results = nil
445      context.data_inspector = proc {
446         |p1, p2, p3, p4|
447         inspect_results = [p1, p2, p3, p4]
448      }
449      func = Function.new context
450      func.create_with_prototype :int, []
451      func.lock {
452         mem_addr = Value.new
453         func.insn_call_alloc_bytearray mem_addr, mk_constant(func, :int, 2)
454         func.insn_store_elem mem_addr, mk_constant(func, :int, 0), mk_constant(func, :int, 32)
455         func.insn_store_elem mem_addr, mk_constant(func, :int, 1), mk_constant(func, :int, 64)
456         func.insn_call_data_inspect mem_addr, mem_addr, mem_addr, mem_addr
457         func.compile
458      }
459      func.apply []
460      assert_equal [256, 256, 256, 256], inspect_results
461   end
463   def test_3
464      context = LoggingContext.new
465      func = Function.new context
466      func.create_with_prototype :int, []
467      func.lock {
468         mem_addr = Value.new
469         func.insn_call_alloc_bytearray mem_addr, mk_constant(func, :int, 4096)
470         func.insn_store_elem mem_addr, mk_constant(func, :int, 0), mk_constant(func, :int, 32)
471         func.insn_store_elem mem_addr, mk_constant(func, :int, 1), mk_constant(func, :int, 64)
472         temp = Value.new
473         func.insn_load_elem temp, mem_addr, mk_constant(func, :int, 0), :int
474         func.insn_call_print_int temp
475         func.insn_load_elem temp, mem_addr, mk_constant(func, :int, 1), :int
476         func.insn_call_print_int temp
477         func.compile
478      }
479      func.apply []
480      assert_equal [32, 64], context.log
481   end
483   def test_2
484      context = LoggingContext.new
485      func = Function.new context
486      func.create_with_prototype :int, []
487      func.lock {
488         counter, temp = Value.new, Value.new
489         func.create_local counter, :int
490         func.fill_value_with_constant temp, :int, 0
491         func.insn_store counter, temp
492         # while loop, with a counter
493         loop_again = Label.new
494         func.insn_label loop_again 
495         func.insn_add temp, counter, mk_constant(func, :int, 1)
496         func.insn_store counter, temp
497         func.insn_call_print_int counter
498         cond = Value.new
499         func.insn_eq cond, counter, mk_constant(func, :int, 5)
500         func.insn_branch_if_not cond, loop_again
501         func.compile
502      }
503      func.apply []
504      assert_equal [1, 2, 3, 4, 5], context.log
505   end
507   def test_1
508      context = LoggingContext.new
509      func = Function.new context
510      func.create_with_prototype :int, []
511      func.lock {
512         ret, val1, val2 = Value.new, Value.new, Value.new
513         func.fill_value_with_constant val1, :int, 2
514         func.fill_value_with_constant val2, :int, 3
515         func.insn_add ret, val1, val2
516         finished = Label.new
517         # test if ne, if not branch to next
518         skip_4 = Label.new
519         cond = Value.new
520         func.insn_ne cond, ret, mk_constant(func, :int, 4)
521         func.insn_branch_if cond, skip_4
522         # else print it and we're finished
523         func.insn_call_print_int mk_constant(func, :int, 4)
524         func.insn_branch finished
525         func.insn_label skip_4
526         # test if ne if not branch to next
527         skip_5 = Label.new
528         func.insn_eq cond, ret, mk_constant(func, :int, 5)
529         func.insn_branch_if_not cond, skip_5
530         # else print it and we're finished
531         func.insn_call_print_int mk_constant(func, :int, 5)
532         func.insn_label skip_5
533         func.insn_label finished
534         func.compile
535      }
536      func.apply []
537      assert_equal [5], context.log
538   end
540   def test_0
541      context = Context.new
542      func = Function.new context
543      func.create_with_prototype :int, []
544      func.lock {
545         ret, val1, val2 = Value.new, Value.new, Value.new
546         func.fill_value_with_constant val1, :int, 2
547         func.fill_value_with_constant val2, :int, 3
548         func.insn_add ret, val1, val2
549         func.insn_return ret
550         func.compile
551      }
552      assert_equal 5, (func.apply [])
553   end