├── MEMO ├── README ├── Rakefile ├── bin └── ytl ├── lib ├── ytl.rb └── ytl │ ├── accmem.rb │ ├── importobj.rb │ ├── macro.rb │ └── thread.rb ├── runtime ├── prelude.rb ├── thread.rb └── type.rb ├── spec ├── fixnum_plus_spec.rb ├── if_spec.rb └── ytlhelper.rb ├── test ├── basictest.rb ├── breaktest.rb ├── classtest.rb ├── exceptiontest.rb ├── execytl.rb ├── exttest.rb ├── floattest.rb ├── hashtest.rb ├── ivtest.rb ├── looptest.rb ├── macrotest.rb ├── regexptest.rb ├── requiretest.rb ├── th_nested.rb ├── threadfib.rb └── threadtest.rb └── ytl.gemspec /MEMO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miura1729/ytl/9b35fd5e68eb9e9b2285586a0ce04b466d97d1d4/MEMO -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1. Waht is ytl 2 | 3 | Ytl is translator from CRuby VM (aka YARV) to X86-32/X86-64 Native Code. 4 | Ytl uses ytljit as code generate library. 5 | 6 | 2. Install 7 | 8 | Install ytljit 9 | gem install ytljit 10 | 11 | Enjoy or take pains 12 | 13 | 3. Sample 14 | 15 | ruby lib/ytl.rb test/basictest.rb 16 | 17 | "copy" # This message is for debug 18 | "copy" # This message is for debug 19 | "copy" # This message is for debug 20 | "copy" # This message is for debug 21 | 1 22 | 1.9 23 | 3 24 | 14930352 25 | 4 26 | 14 27 | 28 | 3. License 29 | 30 | Ruby's 31 | 32 | 4. Author 33 | 34 | Miura Hideki 35 | m-72 at tf6.so-net.ne.jp (e-mail) 36 | http://twitter.com/miura1729 (twitter) 37 | http://d.hatena.ne.jp/miura1729 (blog in japanese) -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # 2 | 3 | require "rbconfig" 4 | require "benchmark" 5 | 6 | ruby_bin = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"]) 7 | BENCH_DIR = "c:/cygwin/home/miura/src/ruby-trunk/ruby/benchmark/" 8 | #BENCH_DIR = "~/cygwin/src/ruby-trunk/ruby/benchmark/" 9 | 10 | desc "run tests" 11 | 12 | task :test do 13 | Dir.glob(File.join("test", "*.rb")) do |f| 14 | sh "#{ruby_bin} -I../ytljit/ext -I../ytljit/lib -I lib/ lib/ytl.rb -r runtime/type.rb " + f 15 | end 16 | end 17 | 18 | cnt = 0 19 | task :bench do 20 | ["bm_so_binary_trees.rb", "bm_so_matrix.rb", "bm_so_array.rb", 21 | "bm_so_ackermann.rb", "bm_so_concatenate.rb", "bm_so_random.rb", 22 | "bm_so_object.rb", "bm_so_nested_loop.rb", "bm_so_sieve.rb", 23 | "bm_so_partial_sums.rb", "bm_so_mandelbrot.rb", "bm_so_nbody.rb", 24 | "bm_so_nsieve.rb", "bm_so_count_words.rb", "bm_so_fannkuch.rb", 25 | "bm_so_lists.rb", "ao-render.rb", 26 | "bm_app_pentomino.rb" , "bm_so_fasta.rb", 27 | ].each do |f| 28 | fn = File.join(BENCH_DIR, f) 29 | Benchmark.benchmark( 30 | " user system total real \n", 20, 31 | "%10.6U %10.6Y %10.6t %10.6r\n" 32 | ) do |x| 33 | print "#{f} \n" 34 | x.report("ytl "){ system "ytl #{fn} > /dev/null" } 35 | x.report("ytl compile "){ system "ytl --compile-only #{fn} > /dev/null" } 36 | if cnt < 17 then 37 | x.report("ytl unboxed "){ system "ytl --compile-array-as-unboxed #{fn} > /dev/null" } 38 | x.report("ytl unbox/inline "){ system "ytl --compile-array-as-unboxed --inline-block #{fn} > /dev/null" } 39 | end 40 | x.report("ruby "){ system "ruby #{fn} > /dev/null" } 41 | cnt += 1 42 | end 43 | end 44 | end 45 | 46 | task :default => [:test] 47 | -------------------------------------------------------------------------------- /bin/ytl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') 3 | require 'ytl' 4 | 5 | opt = YTL::parse_opt(ARGV) 6 | if ARGV[0] == nil 7 | print "#{$0} [OPTS] file.rb \n" 8 | print " use --help for detail \n" 9 | else 10 | YTL::main(opt) 11 | end 12 | -------------------------------------------------------------------------------- /lib/ytl.rb: -------------------------------------------------------------------------------- 1 | require 'ytljit' 2 | require 'ytl/accmem.rb' 3 | require 'ytl/macro.rb' 4 | require 'ytl/importobj.rb' 5 | require 'pp' 6 | require 'optparse' 7 | 8 | include YTLJit 9 | module YTL 10 | include YTLJit 11 | 12 | VERSION = "0.0.6" 13 | 14 | ISEQ_OPTS = { 15 | :peephole_optimization => true, 16 | :inline_const_cache => false, 17 | :specialized_instruction => false, 18 | } 19 | 20 | def self.parse_opt(argv) 21 | ytlopt = {} 22 | ytlopt[:execute_before_compile] = [] 23 | opt = OptionParser.new 24 | 25 | opt.on('-I PATH', 26 | 'Add require/load path') do |f| 27 | $_YTL_LOAD_PATH.push f 28 | end 29 | 30 | opt.on('--disasm', 'Disasemble generated code') do |f| 31 | ytlopt[:disasm] = f 32 | end 33 | 34 | opt.on('--dump-yarv', 'Dump YARV byte code') do |f| 35 | ytlopt[:dump_yarv] = f 36 | end 37 | 38 | opt.on('--disp-signature', 'Display signature of method') do |f| 39 | ytlopt[:disp_signature] = f 40 | end 41 | 42 | opt.on('--insert-signature-comment', 'insert comment of signature') do |f| 43 | ytlopt[:insert_signature_comment] = f 44 | end 45 | 46 | opt.on('--dump-context', 'Dump context(registor/stack) for debug') do |f| 47 | ytlopt[:dump_context] = f 48 | end 49 | 50 | opt.on('-o FILE', '--write-code =FILE', 51 | 'Write generating naitive code and node objects') do |f| 52 | ytlopt[:write_code] = f 53 | end 54 | 55 | opt.on('--write-node-before-type-inference =FILE', 56 | 'Write node of before type inference') do |f| 57 | ytlopt[:write_node_before_ti] = f 58 | end 59 | 60 | opt.on('--write-node-after-type-inference =FILE', 61 | 'Write node of after type inference') do |f| 62 | ytlopt[:write_node_after_ti] = f 63 | end 64 | 65 | opt.on('-r FILE', '--execute-before-compile =FILE', 66 | 'Execute ruby program (execute by CRuby)') do |f| 67 | ytlopt[:execute_before_compile].push f 68 | end 69 | 70 | opt.on('-c', '--compile-only', 71 | 'Stop when compile finished (not execute compiled code)') do |f| 72 | ytlopt[:compile_only] = f 73 | end 74 | 75 | opt.on('--compile-array-as-unboxed', 76 | 'Compile Array as unboxed if nesseary(not excape, not use special methods)') do |f| 77 | ytlopt[:compile_array_as_uboxed] = f 78 | end 79 | 80 | 81 | opt.on('--inline-block', 82 | 'Inline block') do |f| 83 | ytlopt[:inline_block] = f 84 | end 85 | 86 | opt.on('-p', 87 | 'Enable Profile') do |f| 88 | ytlopt[:profile_mode] = f 89 | end 90 | 91 | opt.parse!(argv) 92 | ytlopt 93 | end 94 | 95 | def self.prelude_iseq 96 | prelude = File.join(File.dirname(__FILE__), "..", "runtime", "prelude.rb") 97 | rf = File.read(prelude) 98 | prog = eval(rf) 99 | is = RubyVM::InstructionSequence.compile(prog, prelude, 100 | "", 1, ISEQ_OPTS).to_a 101 | VMLib::InstSeqTree.new(nil, is) 102 | end 103 | 104 | def self.dump_node(tnode, fn) 105 | if defined? yield then 106 | yield 107 | end 108 | File.open(fn, "w") do |fp| 109 | ClassClassWrapper.instance_tab.keys.each do |klass| 110 | if !klass.is_a?(ClassClassWrapper) then 111 | fp.print "class #{klass.name}; end\n" 112 | end 113 | end 114 | 115 | fp.print "Marshal.load(<<'EOS')\n" 116 | fp.print Marshal.dump(tnode) 117 | fp.print "\n" 118 | fp.print "EOS\n" 119 | end 120 | end 121 | 122 | def self.reduced_main(prog, options) 123 | tr_context = VM::YARVContext.new 124 | tr_context.options = options 125 | 126 | import_ruby_object(tr_context) 127 | 128 | tnode = nil 129 | is = RubyVM::InstructionSequence.compile(prog, ARGV[0], 130 | "", 1, ISEQ_OPTS).to_a 131 | iseq = VMLib::InstSeqTree.new(nil, is) 132 | iseqs = [prelude_iseq, iseq] 133 | 134 | tr = VM::YARVTranslatorCRubyObject.new(iseqs) 135 | tnode = tr.translate(tr_context) 136 | 137 | ci_context = VM::CollectInfoContext.new(tnode) 138 | ci_context.options = options 139 | tnode.collect_info(ci_context) 140 | 141 | dmylit = VM::Node::LiteralNode.new(tnode, nil) 142 | dmylit2 = VM::Node::LiteralNode.new(tnode, Object.new) 143 | arg = [dmylit, dmylit, dmylit] 144 | sig = [] 145 | arg.each do |ele| 146 | sig.push RubyType::BaseType.from_ruby_class(NilClass) 147 | end 148 | 149 | ti_context = VM::TypeInferenceContext.new(tnode) 150 | ti_context.options = options 151 | begin 152 | tnode.collect_candidate_type(ti_context, arg, sig) 153 | end until ti_context.convergent 154 | ti_context = tnode.collect_candidate_type(ti_context, arg, sig) 155 | 156 | c_context = VM::CompileContext.new(tnode) 157 | c_context.current_method_signature.push sig 158 | c_context.options = options 159 | c_context = tnode.compile(c_context) 160 | tnode.make_frame_struct_tab 161 | 162 | tcs = tnode.code_space 163 | tcs.call(tcs.base_address) 164 | end 165 | 166 | def self.main(options) 167 | tr_context = VM::YARVContext.new 168 | tr_context.options = options 169 | progs = [] 170 | 171 | import_ruby_object(tr_context) 172 | options[:execute_before_compile].each do |fn| 173 | rf = File.read(fn) 174 | prog = eval(rf) 175 | progs.push prog 176 | is = RubyVM::InstructionSequence.compile(prog, fn, 177 | "", 1, ISEQ_OPTS).to_a 178 | iseq = VMLib::InstSeqTree.new(nil, is) 179 | tr = VM::YARVTranslatorCRubyObject.new([iseq]) 180 | tr_context.current_file_name = fn 181 | tr.translate(tr_context) 182 | end 183 | 184 | tnode = nil 185 | progarray = nil 186 | case File.extname(ARGV[0]) 187 | when ".ytl" 188 | File.open(ARGV[0]) do |fp| 189 | tnode = eval(fp.read, TOPLEVEL_BINDING) 190 | end 191 | tnode.update_after_restore 192 | if tnode.code_space then 193 | tnode.code_space.call(tnode.code_space.base_address) 194 | return 195 | end 196 | 197 | when ".rb" 198 | prog = File.read(ARGV[0]) 199 | if options[:insert_signature_comment] then 200 | progarray = prog.split(/\n/) 201 | end 202 | is = RubyVM::InstructionSequence.compile(prog, ARGV[0], 203 | "", 1, ISEQ_OPTS).to_a 204 | iseq = VMLib::InstSeqTree.new(nil, is) 205 | if options[:dump_yarv] then 206 | pp iseq 207 | end 208 | iseqs = [prelude_iseq, iseq] 209 | 210 | tr = VM::YARVTranslatorCRubyObject.new(iseqs) 211 | tr_context.current_file_name = ARGV[0] 212 | tnode = tr.translate(tr_context) 213 | end 214 | $0 = ARGV[0] 215 | file_name = ARGV[0] 216 | ARGV.shift 217 | 218 | ci_context = VM::CollectInfoContext.new(tnode) 219 | ci_context.options = options 220 | tnode.collect_info(ci_context) 221 | 222 | if fn = options[:write_node_before_ti] then 223 | dump_node(tnode, fn) 224 | end 225 | 226 | dmylit = VM::Node::LiteralNode.new(tnode, nil) 227 | dmylit2 = VM::Node::LiteralNode.new(tnode, Object.new) 228 | arg = [dmylit, dmylit, dmylit] 229 | sig = [] 230 | arg.each do |ele| 231 | sig.push RubyType::BaseType.from_ruby_class(NilClass) 232 | end 233 | 234 | ti_context = VM::TypeInferenceContext.new(tnode) 235 | ti_context.options = options 236 | begin 237 | tnode.collect_candidate_type(ti_context, arg, sig) 238 | end until ti_context.convergent 239 | ti_context = tnode.collect_candidate_type(ti_context, arg, sig) 240 | 241 | if fn = options[:write_node_after_ti] then 242 | dump_node(tnode, fn) 243 | end 244 | 245 | c_context = VM::CompileContext.new(tnode) 246 | c_context.current_method_signature.push sig 247 | c_context.options = options 248 | c_context = tnode.compile(c_context) 249 | tnode.make_frame_struct_tab 250 | 251 | if fn = options[:write_code] then 252 | dump_node(tnode, fn) { 253 | tnode.code_store_hook 254 | } 255 | end 256 | 257 | if options[:disasm] then 258 | tnode.code_space_tab.each do |cs| 259 | cs.fill_disasm_cache 260 | end 261 | tnode.code_space.disassemble 262 | end 263 | 264 | if options[:insert_signature_comment] then 265 | progarray.each_with_index do |lin, lno| 266 | if cinfos = c_context.comment[file_name][lno] # and cinfo[0] then 267 | cinfos.each do |cinfo| 268 | case cinfo[0] 269 | when 1 270 | print "# #{cinfo[1]} \n" 271 | cinfo[2..-1].each do |sig, res| 272 | print "# #{sig[3..-1]} -> #{res.inspect} \n" 273 | print "# self #{sig[2].inspect} \n" 274 | print "# block #{sig[1].inspect} \n" 275 | print "# \n" 276 | end 277 | when 2 278 | print "# #{cinfo[1]} #{cinfo[2].inspect} \n" 279 | when 3 280 | if cinfo[1] == :local_export then 281 | print "# please do't unroll stack \n" 282 | end 283 | end 284 | end 285 | end 286 | print lin, "\n" 287 | end 288 | end 289 | 290 | tcs = tnode.code_space 291 | STDOUT.flush 292 | if !options[:compile_only] then 293 | tcs.call(tcs.base_address) 294 | end 295 | 296 | if options[:profile_mode] then 297 | YTLJit::VM::Node::TraceNode.prof_disp(file_name, prog.split(/\n/)) 298 | end 299 | end 300 | end 301 | 302 | Version = "#{YTL::VERSION} (ytljit #{YTLJit::VERSION})" 303 | $_YTL_FEATURES = [] 304 | $_YTL_LOAD_PATH = $LOAD_PATH 305 | 306 | if __FILE__ == $0 then 307 | # Global variable for require 308 | 309 | YTL::main(YTL::parse_opt(ARGV)) 310 | end 311 | -------------------------------------------------------------------------------- /lib/ytl/accmem.rb: -------------------------------------------------------------------------------- 1 | module YTL 2 | class Memory 3 | end 4 | TheMemory = Memory.new 5 | end 6 | 7 | module YTLJit 8 | module VM 9 | module Node 10 | 11 | module AccMemUtil 12 | include SendUtil 13 | include X86 14 | include X64 15 | 16 | def collect_candidate_type_regident(context, slf) 17 | cursig = context.to_signature 18 | @arguments[3].decide_type_once(cursig) 19 | if slf.ruby_type <= YTL::Memory then 20 | kind = @arguments[4] 21 | kvalue = kind.get_constant_value 22 | 23 | if kvalue then 24 | kvalue =kvalue[0] 25 | 26 | case kvalue 27 | when :char, :word, :dword, :machine_word 28 | fixtype = RubyType::BaseType.from_ruby_class(Fixnum) 29 | add_type(cursig, fixtype) 30 | 31 | when :float 32 | floattype = RubyType::BaseType.from_ruby_class(Float) 33 | add_type(cursig, floattype) 34 | 35 | when AsmType::StructMember 36 | if kvalue.type.is_a?(AsmType::Scalar) then 37 | if kvalue.type.kind == :int then 38 | ttype = RubyType::BaseType.from_ruby_class(Fixnum) 39 | add_type(cursig, ttype) 40 | elsif kvalue.type.kind == :float then 41 | ttype = RubyType::BaseType.from_ruby_class(Float) 42 | add_type(cursig, ttype) 43 | else 44 | raise "Unkown type #{kvalue.type} #{kvalue.type.kind}" 45 | end 46 | else 47 | raise "Unkown type #{kvalue}" 48 | end 49 | 50 | when AsmType::Scalar 51 | if kvalue.kind == :int then 52 | ttype = RubyType::BaseType.from_ruby_class(Fixnum) 53 | add_type(cursig, ttype) 54 | 55 | else 56 | raise "Unkown type" 57 | end 58 | 59 | else 60 | raise "Unkown type #{kvalue}" 61 | 62 | end 63 | 64 | else 65 | fixtype = RubyType::BaseType.from_ruby_class(Fixnum) 66 | add_type(cursig, fixtype) 67 | end 68 | 69 | context 70 | 71 | elsif slf.ruby_type <= AsmType::Pointer or 72 | slf.ruby_type <= AsmType::Array then 73 | tt = AsmType::PointedData 74 | pointedtype = RubyType::BaseType.from_ruby_class(tt) 75 | add_type(cursig, pointedtype) 76 | context 77 | 78 | elsif slf.ruby_type <= AsmType::Struct or 79 | slf.ruby_type <= AsmType::Union or 80 | slf.ruby_type <= AsmType::StructMember then 81 | tt = AsmType::StructMember 82 | stmemtype = RubyType::BaseType.from_ruby_class(tt) 83 | add_type(cursig, stmemtype) 84 | context 85 | 86 | else 87 | super 88 | end 89 | end 90 | 91 | def gen_read_mem(context, type) 92 | size = type.size 93 | asm = context.assembler 94 | case type.kind 95 | when :int 96 | case size 97 | when 8 98 | asm.with_retry do 99 | if context.ret_reg != RAX then 100 | asm.mov(RAX, context.ret_reg) 101 | end 102 | asm.mov(RAX, INDIRECT_RAX) 103 | end 104 | context.ret_reg = RAX 105 | 106 | when 4 107 | asm.with_retry do 108 | if context.ret_reg != EAX then 109 | asm.mov(EAX, context.ret_reg) 110 | end 111 | asm.mov(EAX, INDIRECT_EAX) 112 | end 113 | context.ret_reg = EAX 114 | 115 | when 2 116 | asm.with_retry do 117 | asm.mov(AL, context.ret_reg) 118 | asm.mov(AL, INDIRECT_AL) 119 | end 120 | context.ret_reg = EAX 121 | 122 | else 123 | raise "Unkown Scalar size #{kvalue}" 124 | end 125 | 126 | when :float 127 | case size 128 | when 8 129 | asm.with_retry do 130 | if context.ret_reg != RAX then 131 | asm.mov(RAX, context.ret_reg) 132 | end 133 | asm.movsd(RETFR, INDIRECT_RAX) 134 | end 135 | context.ret_reg = RETFR 136 | 137 | when 4 138 | asm.with_retry do 139 | if context.ret_reg != EAX then 140 | asm.mov(EAX, context.ret_reg) 141 | end 142 | asm.movss(RETFR, INDIRECT_EAX) 143 | end 144 | context.ret_reg = RETFR 145 | 146 | else 147 | raise "Unkown Scalar size #{kvalue}" 148 | end 149 | end 150 | 151 | context 152 | end 153 | 154 | def gen_write_mem(context, type) 155 | size = type.size 156 | asm = context.assembler 157 | case type.kind 158 | when :int 159 | case size 160 | when 8 161 | asm.with_retry do 162 | if context.ret_reg != RAX then 163 | asm.mov(RAX, context.ret_reg) 164 | end 165 | asm.mov(INDIRECT_TMPR2, RAX) 166 | end 167 | context.ret_reg = RAX 168 | 169 | when 4 170 | asm.with_retry do 171 | if context.ret_reg != EAX then 172 | asm.mov(EAX, context.ret_reg) 173 | end 174 | asm.mov(INDIRECT_TMPR2, EAX) 175 | end 176 | context.ret_reg = EAX 177 | 178 | when 2 179 | asm.with_retry do 180 | asm.mov(AL, context.ret_reg) 181 | asm.mov(INDIRECT_TMPR2, AL) 182 | end 183 | context.ret_reg = EAX 184 | 185 | else 186 | raise "Unkown Scalar size #{kvalue}" 187 | end 188 | 189 | when :float 190 | case size 191 | when 8 192 | asm.with_retry do 193 | if context.ret_reg != RETFR then 194 | asm.mov(RETFR, context.ret_reg) 195 | end 196 | asm.movsd(INDIRECT_TMPR2, RETFR) 197 | end 198 | context.ret_reg = RETFR 199 | 200 | when 4 201 | asm.with_retry do 202 | if context.ret_reg != RETFR then 203 | asm.mov(RETFR, context.ret_reg) 204 | end 205 | asm.movss(INDIRECT_TMPR2, RETFR) 206 | end 207 | context.ret_reg = RETFR 208 | 209 | else 210 | raise "Unkown Scalar size #{kvalue}" 211 | end 212 | end 213 | 214 | context 215 | end 216 | 217 | def fill_result_cache(context) 218 | slf = @arguments[2] 219 | slfval = @arguments[2].get_constant_value 220 | if slfval then 221 | slfval =slfval[0] 222 | 223 | idxval = @arguments[3].get_constant_value 224 | if idxval then 225 | idxval = idxval[0] 226 | @result_cache = slfval[idxval] 227 | end 228 | end 229 | 230 | context 231 | end 232 | end 233 | 234 | class SendElementRefMemoryNode> 1) 266 | kvalue = ObjectSpace._id2ref(objid) 267 | end 268 | end 269 | 270 | context = @arguments[3].compile(context) 271 | 272 | case kvalue 273 | when :machine_word 274 | asm.with_retry do 275 | if context.ret_reg != TMPR then 276 | asm.mov(TMPR, context.ret_reg) 277 | end 278 | asm.mov(RETR, INDIRECT_TMPR) 279 | end 280 | context.ret_reg = RETR 281 | 282 | when :float 283 | asm.with_retry do 284 | if context.ret_reg != TMPR then 285 | asm.mov(TMPR, context.ret_reg) 286 | end 287 | asm.mov(RETFR, INDIRECT_TMPR) 288 | end 289 | context.ret_reg = RETFR 290 | 291 | when AsmType::Scalar 292 | context = gen_read_mem(context, kvalue) 293 | 294 | when AsmType::StructMember 295 | typeobj = kvalue.type 296 | case typeobj 297 | when AsmType::Scalar 298 | asm.with_retry do 299 | asm.add(context.ret_reg, kvalue.offset) 300 | end 301 | context = gen_read_mem(context, typeobj) 302 | 303 | else 304 | raise "Unkown Struct Member type #{kvalue}" 305 | end 306 | end 307 | 308 | context.ret_node = self 309 | return context 310 | 311 | elsif slf.type.ruby_type <= AsmType::TypeCommon then 312 | context = @arguments[2].compile(context) 313 | obj = slf.get_constant_value 314 | 315 | if obj == nil and 316 | context.ret_reg.is_a?(OpVarImmidiateAddress) then 317 | objid = (context.ret_reg.value >> 1) 318 | obj = ObjectSpace._id2ref(objid) 319 | else 320 | obj = obj[0] 321 | end 322 | 323 | if obj then 324 | index = @arguments[3].get_constant_value 325 | 326 | if index then 327 | index = index[0] 328 | val = obj[index] 329 | add = lambda { val.address } 330 | context.ret_reg = OpVarImmidiateAddress.new(add) 331 | context.ret_node = self 332 | 333 | return context 334 | end 335 | end 336 | 337 | super 338 | else 339 | super 340 | end 341 | end 342 | end 343 | 344 | class SendElementAssignMemoryNode> 1) 376 | kvalue = ObjectSpace._id2ref(objid) 377 | end 378 | end 379 | 380 | context = @arguments[3].compile(context) 381 | context.start_using_reg(TMPR2) 382 | asm.with_retry do 383 | asm.mov(TMPR2, context.ret_reg) 384 | end 385 | context = @arguments[5].compile(context) 386 | 387 | case kvalue 388 | when :machine_word 389 | asm.with_retry do 390 | if context.ret_reg != RETR then 391 | asm.mov(RETR, context.ret_reg) 392 | end 393 | asm.mov(INDIRECT_TMPR2, RETR) 394 | end 395 | context.ret_reg = RETR 396 | 397 | when :float 398 | asm.with_retry do 399 | if context.ret_reg != RETFR then 400 | asm.mov(RETFR, context.ret_reg) 401 | end 402 | asm.mov(INDIRECT_TMPR, RETFR) 403 | end 404 | context.ret_reg = RETFR 405 | 406 | when AsmType::Scalar 407 | context = gen_write_mem(context, kvalue) 408 | 409 | when AsmType::StructMember 410 | typeobj = kvalue.type 411 | case typeobj 412 | when AsmType::Scalar 413 | asm.with_retry do 414 | asm.add(TMPR2, kvalue.offset) 415 | end 416 | context = gen_write_mem(context, typeobj) 417 | 418 | else 419 | raise "Unkown Struct Member type #{kvalue}" 420 | end 421 | end 422 | 423 | context.ret_node = self 424 | context.end_using_reg(TMPR2) 425 | 426 | @body.compile(context) 427 | else 428 | super 429 | end 430 | end 431 | end 432 | 433 | class SendNewArenaNode 3 and visitsys then 97 | context = vinf.to_ruby(context) 98 | context.ret_code.last << ", " 99 | end 100 | else 101 | visitsys = true 102 | argpos = 0 103 | end 104 | end 105 | context.ret_code.last.chop! 106 | context.ret_code.last.chop! 107 | 108 | context 109 | end 110 | end 111 | 112 | class LocalVarNode 113 | def to_ruby(context) 114 | case kind 115 | when :arg, :local_var 116 | context.ret_code.last << "_#{@name.to_s}" 117 | 118 | when :rest_arg 119 | context.ret_code.last << "*_#{@name.to_s}" 120 | 121 | else 122 | p kind 123 | 124 | end 125 | 126 | context 127 | end 128 | end 129 | 130 | class SystemValueNode 131 | end 132 | 133 | class GuardNode 134 | end 135 | 136 | class MethodEndNode 137 | end 138 | 139 | class BlockEndNode 140 | end 141 | 142 | class ClassEndNode 143 | end 144 | 145 | class SetResultNode 146 | def to_ruby(context) 147 | context.ret_code.last << "break " 148 | context = @value_node.to_ruby(context) 149 | context.ret_code.last << "\n" 150 | @body.to_ruby(context) 151 | end 152 | end 153 | 154 | class PhiNode 155 | def to_ruby(context) 156 | pre = context.work_prefix.join('_') 157 | context.ret_code.last << "#{pre}value" 158 | context 159 | end 160 | end 161 | 162 | class LocalLabel 163 | def to_ruby(context) 164 | context.ret_code.last << "when #{context.jmp_tab[self]}\n" 165 | @body.to_ruby(context) 166 | end 167 | end 168 | 169 | class BranchCommonNode 170 | def to_ruby_common(context, unlessp) 171 | nf = false 172 | if context.jmp_tab[@jmp_to_node] == nil then 173 | context.jmp_tab[@jmp_to_node] = context.jmp_tab.size + 1 174 | nf = true 175 | end 176 | jlab = context.jmp_tab[@jmp_to_node] 177 | 178 | blab = context.jmp_tab.size + 1 179 | context.jmp_tab[@body] = blab 180 | 181 | context.ret_code.push "" 182 | context = @cond.to_ruby(context) 183 | cc = context.ret_code.pop 184 | pre = context.work_prefix.join('_') 185 | context.ret_code.last << "if #{cc} then\n" 186 | if unlessp then 187 | context.ret_code.last << "#{pre}state = #{blab}\n" 188 | context.ret_code.last << "else\n" 189 | context.ret_code.last << "#{pre}state = #{jlab}\n" 190 | else 191 | context.ret_code.last << "#{pre}state = #{jlab}\n" 192 | context.ret_code.last << "else\n" 193 | context.ret_code.last << "#{pre}state = #{blab}\n" 194 | end 195 | context.ret_code.last << "end\n" 196 | 197 | if nf then 198 | context = @jmp_to_node.to_ruby(context) 199 | end 200 | 201 | context.ret_code.last << "when #{context.jmp_tab[@body]}\n" 202 | context = @body.to_ruby(context) 203 | end 204 | end 205 | 206 | class BranchIfNode 207 | def to_ruby(context) 208 | to_ruby_common(context, false) 209 | end 210 | end 211 | 212 | class BranchUnlessNode 213 | def to_ruby(context) 214 | to_ruby_common(context, true) 215 | end 216 | end 217 | 218 | class JumpNode 219 | def to_ruby(context) 220 | nf = false 221 | if context.jmp_tab[@jmp_to_node] == nil then 222 | context.jmp_tab[@jmp_to_node] = context.jmp_tab.size + 1 223 | nf = true 224 | end 225 | pre = context.work_prefix.join('_') 226 | nestat = context.jmp_tab[@jmp_to_node] 227 | context.ret_code.last << "#{pre}state = #{nestat}\n" 228 | valnode = @jmp_to_node.come_from[self] 229 | if valnode then 230 | context.ret_code.push "" 231 | context = valnode.to_ruby(context) 232 | val = context.ret_code.pop 233 | context.ret_code.last << "#{pre}value = #{val}\n" 234 | end 235 | if nf then 236 | @jmp_to_node.to_ruby(context) 237 | else 238 | context 239 | end 240 | end 241 | end 242 | 243 | class ThrowNode 244 | def to_ruby(context) 245 | case @state 246 | when 1 # return 247 | context.ret_code.last << "return " 248 | when 2 # break 249 | context.ret_code.last << "break " 250 | else # raise 251 | context.ret_code.last << "raise " 252 | end 253 | context.ret_code.last << "(" 254 | context = @exception_object.to_ruby(context) 255 | context.ret_code.last << ")\n" 256 | context 257 | end 258 | end 259 | 260 | class LetNode 261 | end 262 | 263 | class LiteralNode 264 | def to_ruby(context) 265 | context.ret_code.last << get_constant_value[0].inspect 266 | context 267 | end 268 | end 269 | 270 | class TraceNode 271 | def to_ruby(context) 272 | @body.to_ruby(context) 273 | end 274 | end 275 | 276 | class ClassValueNode 277 | end 278 | 279 | class SpecialObjectNode 280 | end 281 | 282 | class YieldNode 283 | def to_ruby(context) 284 | arg = @parent.arguments 285 | context.ret_code.last << "yield" 286 | context.ret_code.last << "(" 287 | arg[3..-1].each do |ae| 288 | context = ae.to_ruby(context) 289 | context.ret_code.last << ", " 290 | end 291 | context.ret_code.last.chop! 292 | context.ret_code.last.chop! 293 | context.ret_code.last << ")\n" 294 | context 295 | end 296 | end 297 | 298 | class FixArgCApiNode 299 | API_TAB = { 300 | "rb_str_append" => "+", 301 | "rb_obj_as_string" => "to_s", 302 | } 303 | def to_ruby(context) 304 | arg = @parent.arguments 305 | context.ret_code.last << "(" 306 | context = arg[0].to_ruby(context) 307 | context.ret_code.last << "." 308 | context.ret_code.last << API_TAB[@name] 309 | if arg[1] then 310 | context.ret_code.last << "(" 311 | arg[1..-1].each do |ae| 312 | context = ae.to_ruby(context) 313 | context.ret_code.last << ", " 314 | end 315 | context.ret_code.last.chop! 316 | context.ret_code.last.chop! 317 | context.ret_code.last << ")" 318 | end 319 | context.ret_code.last << ")\n" 320 | context 321 | end 322 | end 323 | 324 | class MethodSelectNode 325 | def to_ruby(context) 326 | arg = @parent.arguments 327 | context.ret_code.last << "(" 328 | name = @name 329 | callee = nil 330 | if @parent.is_fcall or @parent.is_vcall then 331 | callee = SendNode.get_macro_tab[name] 332 | if callee then 333 | name = ("ytl__eval_" + name.to_s).to_sym 334 | end 335 | context.ret_code.last << name.to_s 336 | else 337 | context = arg[2].to_ruby(context) 338 | context.ret_code.last << ".#{name}" 339 | end 340 | if arg[3] then 341 | context.ret_code.last << "(" 342 | arg[3..-1].each do |ae| 343 | context = ae.to_ruby(context) 344 | context.ret_code.last << ", " 345 | end 346 | context.ret_code.last.chop! 347 | context.ret_code.last.chop! 348 | context.ret_code.last << ")" 349 | end 350 | if arg[1].is_a?(BlockTopNode) then 351 | context.ret_code.last << " " 352 | context.work_prefix.push "bl" 353 | context = arg[1].to_ruby(context) 354 | context.work_prefix.pop 355 | end 356 | if callee then 357 | context.ret_code.last << "[0]" 358 | callee.each do |rec, cele| 359 | if cele.is_a?(BaseNode) then 360 | SendNode.get_macro_tab[@name][rec] = true 361 | tcontext = ToRubyContext.new 362 | code = cele.to_ruby(tcontext).ret_code.last 363 | proc = eval("lambda" + code) 364 | SendNode.get_macro_tab[@name][rec] = proc 365 | end 366 | end 367 | end 368 | context.ret_code.last << ")\n" 369 | context 370 | end 371 | end 372 | 373 | class VariableRefCommonNode 374 | end 375 | 376 | class LocalVarRefCommonNode 377 | end 378 | 379 | class LocalVarRefNode 380 | def to_ruby(context) 381 | cfi = @current_frame_info 382 | off = cfi.real_offset(@offset) 383 | lv = cfi.frame_layout[off] 384 | context.ret_code.last << " _#{lv.name.to_s} " 385 | context 386 | end 387 | end 388 | 389 | class SelfRefNode 390 | def to_ruby(context) 391 | context.ret_code.last << " self " 392 | context 393 | end 394 | end 395 | 396 | class LocalAssignNode 397 | def to_ruby(context) 398 | cfi = @current_frame_info 399 | off = cfi.real_offset(@offset) 400 | lv = cfi.frame_layout[off] 401 | context.ret_code.last << "_#{lv.name.to_s} = " 402 | context = @val.to_ruby(context) 403 | context.ret_code.last << "\n" 404 | @body.to_ruby(context) 405 | end 406 | end 407 | 408 | class InstanceVarRefCommonNode 409 | end 410 | 411 | class InstanceVarRefNode 412 | def to_ruby(context) 413 | context.ret_code.last << " #{@name.to_s} " 414 | context 415 | end 416 | end 417 | 418 | class InstanceVarAssignNode 419 | def to_ruby(context) 420 | context.ret_code.last << "#{@name.to_s} = " 421 | context = @val.to_ruby(context) 422 | context.ret_code.last << "\n" 423 | context 424 | end 425 | end 426 | 427 | class GlobalVarRefNode 428 | def to_ruby(context) 429 | context.ret_code.last << "#{@name.to_s}" 430 | context 431 | end 432 | end 433 | 434 | class ConstantRefNode 435 | def to_ruby(context) 436 | context.ret_code.last << @name.to_s 437 | context 438 | end 439 | end 440 | 441 | class ConstantAssignNode 442 | def to_ruby(context) 443 | context.ret_code.last << "#{@name.to_s} = " 444 | context = @val.to_ruby(context) 445 | context.ret_code.last << "\n" 446 | context 447 | end 448 | end 449 | 450 | class SendNode 451 | def to_ruby(context) 452 | context = @func.to_ruby(context) 453 | @body.to_ruby(context) 454 | end 455 | end 456 | 457 | class SendEvalNode 458 | def to_ruby(context) 459 | context.ret_code.last << "evalstr <<" 460 | context = @arguments[3].to_ruby(context) 461 | context.ret_code.last << "\n" 462 | @body.to_ruby(context) 463 | end 464 | end 465 | end 466 | end 467 | end 468 | -------------------------------------------------------------------------------- /lib/ytl/thread.rb: -------------------------------------------------------------------------------- 1 | module YTLJit 2 | module VM 3 | module TypeCodeGen 4 | module YTLJitRuntimeThreadTypeBoxedCodeGen 5 | include TypeUtil 6 | def instance 7 | ni = self.dup 8 | ni.instance_eval { extend YTLJitRuntimeThreadTypeBoxedCodeGen } 9 | ni.init 10 | ni 11 | end 12 | 13 | def init 14 | @element_type = nil 15 | end 16 | 17 | attr_accessor :element_type 18 | 19 | def gen_copy(context) 20 | context 21 | end 22 | 23 | def have_element? 24 | true 25 | end 26 | 27 | def inspect 28 | "{#{@ruby_type} self=#{@element_type.inspect}}" 29 | end 30 | end 31 | end 32 | 33 | module Node 34 | class SimpleVectorRefNode= 3 then 93 | nnode = SimpleVectorRefNode.new(self, idx + 3, TMPR2) 94 | 95 | else 96 | nnode = SimpleVectorRefNode.new(self, idx + 1, TMPR2) 97 | end 98 | @block_args.push nnode 99 | end 100 | func = DirectBlockNode.new(self, @arguments[1]) 101 | @yield_node = SendNode.new(self, func, @block_args, 0, 0) 102 | @modified_instance_var = nil 103 | @curpare = [nil, []] 104 | end 105 | 106 | def collect_info(context) 107 | context.modified_instance_var[:@_prev_self] ||= [] 108 | context.modified_instance_var[:@_prev_self].push @curpare 109 | @modified_instance_var = context.modified_instance_var 110 | @arguments.each do |arg| 111 | context = arg.collect_info(context) 112 | end 113 | context = @func.collect_info(context) 114 | @body.collect_info(context) 115 | end 116 | 117 | def collect_candidate_type_regident(context, slf) 118 | slfcls = @arguments[2].get_constant_value 119 | tt = RubyType::BaseType.from_ruby_class(slfcls[0]) 120 | if tt.ruby_type == Runtime::Thread then 121 | cursig = context.to_signature 122 | slfoff = @frame_info.real_offset(2) 123 | slfnode = @frame_info.frame_layout[slfoff] 124 | blknode = @arguments[1] 125 | argnode =[@arguments[0], blknode, slfnode, *@arguments[3..-1]] 126 | argnode.zip(@block_args) do |bele, oele| 127 | same_type(oele, bele, cursig, cursig, context) 128 | end 129 | 130 | yargs = @block_args 131 | context = @yield_node.collect_candidate_type(context) 132 | ysignat = @yield_node.signature(context) 133 | context = blknode.collect_candidate_type(context, yargs, ysignat) 134 | 135 | tt = RubyType::BaseType.from_ruby_class(Runtime::Thread) 136 | add_type(cursig, tt) 137 | joinsig = cursig.dup 138 | joinsig[1] = RubyType::BaseType.from_ruby_class(NilClass) 139 | joinsig[2] = tt 140 | add_element_node(tt, cursig, slfnode, nil, context) 141 | add_element_node(tt, ysignat, slfnode, nil, context) 142 | same_type(slfnode, slfnode, ysignat, cursig, context) 143 | add_element_node(tt, joinsig, slfnode, nil, context) 144 | same_type(slfnode, slfnode, joinsig, cursig, context) 145 | 146 | @curpare[0] = slfnode 147 | if !@curpare[1].include?(cursig) then 148 | @curpare[1].push cursig 149 | end 150 | 151 | return context 152 | end 153 | 154 | return super 155 | end 156 | 157 | def compile(context) 158 | rect = @arguments[2].decide_type_once(context.to_signature) 159 | rrtype = rect.ruby_type_raw 160 | if rrtype.is_a?(ClassClassWrapper) then 161 | rrtype = get_singleton_class_object(@arguments[2]).ruby_type 162 | if rrtype == Runtime::Thread then 163 | cursig = context.to_signature 164 | 165 | # Generate block invoker 166 | tcontext = context.dup 167 | tcontext.set_code_space(@block_cs) 168 | asm = tcontext.assembler 169 | asm.with_retry do 170 | asm.mov(TMPR, FUNC_ARG[0]) 171 | end 172 | tcontext.start_using_reg(TMPR2) 173 | asm.with_retry do 174 | asm.mov(TMPR2, TMPR) 175 | asm.mov(THEPR, INDIRECT_TMPR2) 176 | end 177 | tcontext = @yield_node.compile(tcontext) 178 | tcontext.end_using_reg(TMPR2) 179 | addr = lambda { 180 | a = address_of('ytl_thread_exit') 181 | $symbol_table[a] = 'yth_thread_exit' 182 | a 183 | } 184 | thread_exit = OpVarMemAddress.new(addr) 185 | asm.with_retry do 186 | asm.push(tcontext.ret_reg) 187 | asm.call(thread_exit) 188 | # never reach here 189 | end 190 | 191 | # Compile to call ytl_thread_create 192 | addr = lambda { 193 | a = address_of('ytl_thread_create') 194 | $symbol_table[a] = 'yth_thread_create' 195 | a 196 | } 197 | thread_create = OpVarMemAddress.new(addr) 198 | 199 | addr = lambda { 200 | a = address_of('ytl_ivar_set_boxing') 201 | $symbol_table[a] = 'yth_ivar_set_boxing' 202 | a 203 | } 204 | ivar_set = OpVarMemAddress.new(addr) 205 | ivaroff = @modified_instance_var.keys.index(:@_prev_self) 206 | orgslf = OpIndirect.new(SPR, AsmType::MACHINE_WORD.size) 207 | 208 | asm = context.assembler 209 | 210 | # have alternate arguments 211 | if @arguments.size > 3 then 212 | @arguments[3..-1].each do |ele| 213 | context = ele.compile(context) 214 | asm.with_retry do 215 | asm.mov(TMPR, context.ret_reg) 216 | asm.push(TMPR) 217 | end 218 | end 219 | end 220 | 221 | context.start_using_reg(TMPR2) 222 | 223 | asm.with_retry do 224 | asm.mov(TMPR, @frame_info.offset_arg(2, BPR)) 225 | asm.push(TMPR) 226 | end 227 | context.ret_reg = TMPR 228 | context = cursig[2].gen_copy(context) 229 | 230 | asm.with_retry do 231 | asm.push(context.ret_reg) 232 | 233 | # write prev self to copyed self 234 | asm.mov(TMPR2, orgslf) 235 | asm.mov(FUNC_ARG[0], context.ret_reg) 236 | asm.mov(FUNC_ARG[1], ivaroff) 237 | asm.mov(FUNC_ARG[2], TMPR2) 238 | asm.call_with_arg(ivar_set, 3) 239 | 240 | asm.mov(TMPR, @frame_info.offset_arg(1, BPR)) 241 | asm.push(TMPR) # block addr 242 | asm.push(BPR) # oldbp 243 | asm.push(THEPR) 244 | asm.mov(TMPR, SPR) 245 | asm.mov(FUNC_ARG[0], TMPR) 246 | asm.mov(TMPR, @block_cs.var_base_immidiate_address) 247 | asm.mov(FUNC_ARG[1], TMPR) 248 | end 249 | context = gen_call(context, thread_create, 2) 250 | asm.with_retry do 251 | asm.add(SPR, AsmType::MACHINE_WORD.size * 5) 252 | end 253 | 254 | context.end_using_reg(TMPR2) 255 | context.ret_reg = RETR 256 | context.ret_node = self 257 | 258 | return context 259 | else 260 | super 261 | end 262 | else 263 | super 264 | end 265 | end 266 | end 267 | end 268 | end 269 | end 270 | -------------------------------------------------------------------------------- /runtime/prelude.rb: -------------------------------------------------------------------------------- 1 | # runtime library written in ytl 2 | <<-'EOS' 3 | # 4 | class Module 5 | def attr(*x) 6 | x.each do |ele| 7 | eval "def #{ele}; @#{ele} ; end" 8 | end 9 | end 10 | 11 | def attr_accessor(*x) 12 | x.each do |ele| 13 | eval "def #{ele}; @#{ele} ; end\n" 14 | eval "def #{ele}=(val); @#{ele} = val ; end" 15 | end 16 | end 17 | end 18 | 19 | class Object 20 | def require(fn) 21 | ff = nil 22 | $_YTL_LOAD_PATH.each do |dir| 23 | f = dir + "/" + fn 24 | 25 | f2 = f 26 | if $_YTL_FEATURES.include?(f2) then 27 | ff = true 28 | next 29 | end 30 | if File.file?(f2) then 31 | $_YTL_FEATURES.push f2 32 | fp = open(f2) 33 | a = fp.read 34 | eval a 35 | fp.close 36 | ff = true 37 | break 38 | end 39 | 40 | f2 = f + ".rb" 41 | if $_YTL_FEATURES.include?(f2) then 42 | ff = true 43 | next 44 | end 45 | if File.file?(f2) then 46 | p f2 47 | $_YTL_FEATURES.push f2 48 | fp = open(f2) 49 | a = fp.read 50 | eval a 51 | fp.close 52 | ff = true 53 | break 54 | end 55 | 56 | f2 = f + ".so" 57 | if $_YTL_FEATURES.include?(f2) then 58 | ff = true 59 | next 60 | end 61 | if File.file?(f2) then 62 | $_YTL_FEATURES.push f2 63 | # require f2 64 | ff = true 65 | break 66 | end 67 | end 68 | if ff == nil then 69 | raise "No such file #{fn}" 70 | end 71 | end 72 | end 73 | 74 | class Array 75 | def each 76 | i = 0 77 | e = self.size 78 | while i < e 79 | yield self[i] 80 | i = i + 1 81 | end 82 | 83 | self 84 | end 85 | 86 | def each_index 87 | i = 0 88 | e = self.size 89 | while i < e 90 | yield i 91 | i = i + 1 92 | end 93 | 94 | self 95 | end 96 | 97 | def collect 98 | res = [] 99 | i = 0 100 | e = self.size 101 | while i < e 102 | res[i] = yield self[i] 103 | i = i + 1 104 | end 105 | 106 | res 107 | end 108 | 109 | def map 110 | res = [] 111 | i = 0 112 | e = self.size 113 | while i < e 114 | res[i] = yield self[i] 115 | i = i + 1 116 | end 117 | 118 | res 119 | end 120 | 121 | def find 122 | i = 0 123 | e = self.size 124 | while i < e 125 | if yield self[i] then 126 | return self[i] 127 | end 128 | i = i + 1 129 | end 130 | 131 | return nil 132 | end 133 | 134 | def at(idx) 135 | self[idx] 136 | end 137 | 138 | def first 139 | self[0] 140 | end 141 | end 142 | 143 | class Range 144 | def each 145 | i = self.first 146 | e = self.last 147 | if self.exclude_end? then 148 | while i < e 149 | yield i 150 | i = i + 1 151 | end 152 | else 153 | while i <= e 154 | yield i 155 | i = i + 1 156 | end 157 | end 158 | 159 | self 160 | end 161 | 162 | def collect 163 | res = [] 164 | rp = 0 165 | =begin 166 | self.each do |n| 167 | res[rp] = yield n 168 | rp = rp + 1 169 | end 170 | =end 171 | #=begin 172 | i = self.first 173 | e = self.last 174 | if self.exclude_end? then 175 | while i < e 176 | res[rp] = yield i 177 | rp = rp + 1 178 | i = i + 1 179 | end 180 | else 181 | while i <= e 182 | res[rp] = yield i 183 | rp = rp + 1 184 | i = i + 1 185 | end 186 | end 187 | #=end 188 | 189 | res 190 | end 191 | 192 | def to_a 193 | i = self.first 194 | e = self.last 195 | k = 0 196 | siz = e - i + 1 197 | res = Array.new(siz, 0) 198 | if self.exclude_end? then 199 | while i < e 200 | res[k] = i 201 | k = k + 1 202 | i = i + 1 203 | end 204 | else 205 | while i <= e 206 | res[k] = i 207 | k = k + 1 208 | i = i + 1 209 | end 210 | end 211 | 212 | res 213 | end 214 | end 215 | 216 | class Fixnum 217 | def times 218 | i = 0 219 | while i < self 220 | yield i 221 | i = i + 1 222 | end 223 | 224 | self 225 | end 226 | 227 | def upto(n) 228 | i = self 229 | while i <= n 230 | yield i 231 | i = i + 1 232 | end 233 | 234 | self 235 | end 236 | 237 | def downto(n) 238 | i = self 239 | while i >= n 240 | yield i 241 | i = i - 1 242 | end 243 | 244 | self 245 | end 246 | 247 | def step(max, st) 248 | i = self 249 | if st > 0 then 250 | while i <= max 251 | yield i 252 | i = i + st 253 | end 254 | else 255 | while i >= max 256 | yield i 257 | i = i + st 258 | end 259 | end 260 | 261 | self 262 | end 263 | 264 | def abs 265 | if self < 0 then 266 | -self 267 | else 268 | self 269 | end 270 | end 271 | 272 | def **(n) 273 | a = 1 274 | while n > 0 275 | a = a * self 276 | n = n - 1 277 | end 278 | a 279 | end 280 | end 281 | 282 | class IO 283 | def each_line 284 | while str = gets 285 | yield str 286 | end 287 | nil 288 | end 289 | end 290 | 291 | EOS 292 | 293 | -------------------------------------------------------------------------------- /runtime/thread.rb: -------------------------------------------------------------------------------- 1 | require 'ytl/thread.rb' 2 | include YTLJit 3 | include InternalRubyType 4 | 5 | tr_context.import_object(YTLJit::Runtime, :Thread, Runtime::Thread) 6 | <<-'EOS' 7 | # 8 | 9 | def self_merge(cself, pself) 10 | cself 11 | end 12 | 13 | module YTLJit 14 | module Runtime 15 | class Thread 16 | def join 17 | _join 18 | caller = self_of_caller 19 | newself = caller.self_merge(self.cself, self.pself) 20 | _merge(newself) 21 | end 22 | end 23 | end 24 | end 25 | 26 | EOS 27 | 28 | -------------------------------------------------------------------------------- /runtime/type.rb: -------------------------------------------------------------------------------- 1 | include YTLJit 2 | include InternalRubyType 3 | 4 | tr_context.import_object(YTLJit, :AsmType, YTLJit::AsmType) 5 | tr_context.import_object(YTLJit, :Runtime, YTLJit::Runtime) 6 | tr_context.import_object(YTLJit::AsmType, :VALUE, VALUE) 7 | tr_context.import_object(YTLJit::AsmType, :P_CHAR, P_CHAR) 8 | tr_context.import_object(YTLJit::AsmType, :RBasic, RBasic) 9 | tr_context.import_object(YTLJit::AsmType, :RString, RString) 10 | tr_context.import_object(YTLJit::AsmType, :RFloat, RFloat) 11 | tr_context.import_object(YTLJit::Runtime, :Arena, Runtime::Arena) 12 | "" 13 | -------------------------------------------------------------------------------- /spec/fixnum_plus_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ytlhelper' 2 | 3 | describe "Fixnum#+" do 4 | it "returns self +plus the given positive Integer" do 5 | prog = "491 + 2" 6 | prog.execute_ytl.should == prog.execute_ruby 7 | prog = "90210 + 10" 8 | prog.execute_ytl.should == prog.execute_ruby 9 | end 10 | 11 | it "returns self +plus the given negative Integer" do 12 | prog = "491 + -2" 13 | prog.execute_ytl.should == prog.execute_ruby 14 | prog = "90210 + -10" 15 | prog.execute_ytl.should == prog.execute_ruby 16 | prog = "-90210 + -10" 17 | prog.execute_ytl.should == prog.execute_ruby 18 | prog = "-90210 + 10" 19 | prog.execute_ytl.should == prog.execute_ruby 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/if_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ytlhelper' 2 | 3 | describe "The if expression" do 4 | it "evaluate body if expression is true" do 5 | prog = "a = []; if true then a = [123]; end; a" 6 | prog.execute_ytl.should == prog.execute_ruby 7 | end 8 | 9 | it "does not evaluate body if expression is false" do 10 | prog = "a = []; if false then a = [123]; end; a" 11 | prog.execute_ytl.should == prog.execute_ruby 12 | end 13 | 14 | it "does not evaluate body if expression is false" do 15 | prog = "a = []; if false then a = [123]; end; a" 16 | prog.execute_ytl.should == prog.execute_ruby 17 | end 18 | 19 | it "does not evaluate body if expression is true" do 20 | prog = "a = []; if () then a = [123]; end; a" 21 | prog.execute_ytl.should == prog.execute_ruby 22 | end 23 | 24 | it "does not evaluate else-body if expression is true" do 25 | prog = "a = []; if true then a = [123]; else [456]; end; a" 26 | prog.execute_ytl.should == prog.execute_ruby 27 | end 28 | 29 | it "evaluate only else-body if expression is false" do 30 | prog = "a = []; if false then a = [123]; else [456]; end; a" 31 | prog.execute_ytl.should == prog.execute_ruby 32 | end 33 | 34 | it "returns result of then-body evaluation if expression is true" do 35 | prog = "if true then [123] end" 36 | prog.execute_ytl.should == prog.execute_ruby 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/ytlhelper.rb: -------------------------------------------------------------------------------- 1 | require 'ytl' 2 | 3 | class String 4 | def execute_ytl 5 | YTL::reduced_main(self, {}) 6 | end 7 | 8 | def execute_ruby 9 | eval(self) 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /test/basictest.rb: -------------------------------------------------------------------------------- 1 | def id (x) 2 | x 3 | end 4 | 5 | p id(1) 6 | p id(1.9) 7 | 8 | def array 9 | a = [3, 5, 6] 10 | p a 11 | a[1] = 1 12 | p a 13 | [1, 2, 3][0] + [1, 2, 3][1] 14 | end 15 | p array 16 | 17 | def array2 18 | b = 3 19 | a = [3, b, 6] 20 | p a 21 | a[1] = 1 22 | p a 23 | [1, 2, 2 * 3][0] + [1 * 2, 2 * 4, 3][1] 24 | end 25 | p array2 26 | 27 | def fib(x) 28 | if x < 2 then 29 | 1 30 | else 31 | fib(x-1) + fib(x-2) 32 | end 33 | end 34 | 35 | p fib(35) 36 | 37 | def fib2(x) 38 | if x < 2 then 39 | 1.0 40 | else 41 | fib2(x-1) + fib2(x-2) 42 | end 43 | end 44 | 45 | p fib2(35) 46 | 47 | def blk0(x) 48 | yield(x) + 2 49 | end 50 | 51 | def blk1(x) 52 | yield(x) + 10 53 | end 54 | 55 | p blk0(1) {|a| a + 1} 56 | p blk1(1) {|a| blk0(a) {|b| b + a}} 57 | 58 | def blk3 59 | yield 60 | end 61 | 62 | p id(blk3 { "abc"}) 63 | p id(blk3 { 1 }) 64 | p blk3 { 1 } 65 | 66 | def mul(x, y) 67 | x * y 68 | end 69 | 70 | p "mul" 71 | p mul(30, 40) 72 | p mul(30, -40) 73 | p mul(-30, 40) 74 | p mul(-30, -40) 75 | p mul(30.0, 40.0) 76 | p mul(30.0, -40.0) 77 | p mul(-30.0, 40.0) 78 | p mul(-30.0, -40.0) 79 | 80 | def div(x, y) 81 | x / y 82 | end 83 | 84 | p "div" 85 | p div(30, 4) 86 | p div(30, -4) 87 | p div(-30, 4) 88 | p div(-30, -4) 89 | p div(30.0, 7.0) 90 | p div(30.0, -7.0) 91 | p div(-30.0, 7.0) 92 | p div(-30.0, -7.0) 93 | p div(35, 7) 94 | p div(35, -7) 95 | p div(-35, 7) 96 | p div(-35, -7) 97 | 98 | def rem(x, y) 99 | x % y 100 | end 101 | p "rem" 102 | p rem(30, 4) 103 | p rem(30, -4) 104 | p rem(-30, 4) 105 | p rem(-30, -4) 106 | p rem(35, 7) 107 | p rem(35, -7) 108 | p rem(-35, 7) 109 | p rem(-35, -7) 110 | 111 | p "shift" 112 | p 1 << 2 113 | p 1 << 0 114 | p 3 >> 1 115 | p 1024 >> 3 116 | 117 | p 1 < 1 118 | p 1 > 1 119 | p 1 <= 1 120 | p 1 >= 1 121 | 122 | p 1 < 2 123 | p 1 > 2 124 | p 1 <= 2 125 | p 1 >= 2 126 | 127 | p 1.0 < 2.0 128 | p 1.0 > 2.0 129 | p 1.0 <= 2.0 130 | p 1.0 >= 2.0 131 | 132 | p 1.0 < 1e-17 133 | p 1.0 > 1e-17 134 | p 1.0 <= 1e-17 135 | p 3.0 > 2.0 136 | p :foo 137 | p :foo == :foo 138 | p :foo == :bar 139 | p :foo != :foo 140 | p :foo != :bar 141 | 142 | def multi_type_var 143 | a = 1 144 | p a 145 | a = "a" 146 | p a 147 | a = 1.0 148 | p a 149 | # p 1.0 150 | end 151 | 152 | multi_type_var 153 | 154 | def test_while 155 | i = 10 156 | j = 0 157 | k = 10 158 | 159 | while i > 0 160 | while k > 0 161 | j = j + i 162 | k = k - 1 163 | end 164 | i = i - 1 165 | k = 10 166 | end 167 | p j 168 | 169 | i = 10 170 | j = 0 171 | while i > 0 172 | i = i - 1 173 | j = j + i 174 | end 175 | p j 176 | end 177 | 178 | test_while 179 | 180 | i = 5 181 | a = i..7 182 | p a.first 183 | p a.last 184 | p a 185 | p Range.new(i, 2, false) 186 | p 1...3 187 | 188 | def test_poly(a, b, c) 189 | if c == 1 then 190 | a + b 191 | else 192 | a * b 193 | end 194 | end 195 | 196 | p test_poly(2, 2, 1) 197 | p test_poly(2.0, 2.0, 1) 198 | p test_poly(2, 2, 0) 199 | p test_poly(2.0, 2.0, 0) 200 | 201 | p "test for swap" 202 | a = 1 203 | b = 3 204 | a, b = b + 1, a 205 | p a 206 | p b 207 | 208 | p "test for is_a?" 209 | p 1.is_a?(Float) 210 | p 1.9.is_a?(Float) 211 | p 1.is_a?(Fixnum) 212 | p 1.is_a?(Object) 213 | 214 | def dummy_p(val) 215 | if val.is_a?(Fixnum) 216 | p "Fixnum #{val}" 217 | elsif val.is_a?(Float) 218 | p "Float #{val}" 219 | elsif val.is_a?(String) 220 | p "String #{val}" 221 | end 222 | end 223 | dummy_p(1) 224 | dummy_p(1.5) 225 | dummy_p("1.5") 226 | 227 | def foo2(a, b, c) 228 | p a 229 | p b 230 | p c 231 | c.disp_type 232 | end 233 | a = [1, "2", 3] 234 | a[1] = "a" 235 | foo2(*a) 236 | b = [] 237 | c = [1, "c", 1.2] 238 | #c = [1, 23, 4] 239 | i = 0 240 | c.disp_type 241 | while i < 3 242 | b[i] = c[i] 243 | i = i + 1 244 | end 245 | foo2(*b) 246 | 247 | =begin 248 | for i in 1..2 249 | p i 250 | end 251 | 252 | for i in 1...2 253 | p i 254 | end 255 | 256 | =end 257 | 258 | -------------------------------------------------------------------------------- /test/breaktest.rb: -------------------------------------------------------------------------------- 1 | def test0 2 | begin 3 | return false 4 | ensure 5 | p "BAR0" 6 | end 7 | end 8 | 9 | def test1 10 | [1, 2, 3].each do |n| 11 | p n 12 | next 13 | end 14 | p "foo1" 15 | end 16 | 17 | def test2 18 | [1, 2, 3].each do |n| 19 | p n 20 | return false 21 | end 22 | p "bar2" 23 | end 24 | 25 | def test3 26 | begin 27 | [1, 2, 3].each do |n| 28 | p n 29 | return false 30 | end 31 | p "foo3" 32 | ensure 33 | p "bar3" 34 | end 35 | end 36 | 37 | def test4 38 | begin 39 | [1, 2, 3].each do |n| 40 | p n 41 | break 42 | end 43 | p "foo4" 44 | ensure 45 | p "bar4" 46 | end 47 | end 48 | 49 | def test42 50 | begin 51 | [1, 2, 3].each do |n| 52 | p n 53 | break 54 | end 55 | p "foo42" 56 | end 57 | end 58 | 59 | 60 | p test0 61 | 62 | p test1 63 | p test2 64 | 65 | p test3 66 | p test42 67 | p test4 68 | 69 | =begin 70 | # Not support yet 71 | 72 | def test5 73 | begin 74 | [1, 2, 3].each do |n| 75 | p n 76 | next 77 | end 78 | p "foo5" 79 | ensure 80 | p "bar5" 81 | end 82 | end 83 | 84 | def test6 85 | begin 86 | [1, 2, 3].each do |n| 87 | p n 88 | redo 89 | end 90 | p "foo" 91 | ensure 92 | p "bar" 93 | end 94 | end 95 | =end 96 | 97 | def test7 98 | [1, 2, 3].each do |m| 99 | [4, 5, 6].each do |n| 100 | p m 101 | p n 102 | return false if n + m == 7 103 | end 104 | p "bar7-1" 105 | end 106 | p "bar7-2" 107 | end 108 | 109 | p test7 110 | -------------------------------------------------------------------------------- /test/classtest.rb: -------------------------------------------------------------------------------- 1 | class Foo 2 | def bar 3 | p "Foo#bar" 4 | end 5 | 6 | def baz 7 | p "Foo#baz" 8 | p @a 9 | end 10 | 11 | def initialize 12 | @a = 4 13 | p "initialize Foo" 14 | p self 15 | end 16 | end 17 | 18 | class Bar 1, :b => 3, 1 => :c} 10 | p b 11 | p b[:a] 12 | p b[:b] 13 | p b[3] 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/ivtest.rb: -------------------------------------------------------------------------------- 1 | DAYS_PER_YEAR = 365.24 2 | 3 | class Foo 4 | def initialize(x) 5 | @a = x * DAYS_PER_YEAR 6 | @b = 10.0 7 | end 8 | 9 | attr_accessor :a, :b 10 | 11 | def mov(bodies) 12 | # bodies[0].a += @a 13 | p bodies[0].a 14 | end 15 | end 16 | 17 | foo = Foo.new(1.4) 18 | foo.mov([Foo.new(2.8)]) 19 | p [Foo.new(2.8)][0] 20 | -------------------------------------------------------------------------------- /test/looptest.rb: -------------------------------------------------------------------------------- 1 | 10.times do |i| 2 | i.times do |j| 3 | p i 4 | j.times do |k| 5 | p k 6 | end 7 | end 8 | end 9 | 10 | a = 5 11 | for i in a...10 12 | for j in a..7 13 | p i 14 | end 15 | end 16 | 17 | p a..10 18 | for i in a..10 19 | # for j in a..20 20 | p i 21 | # end 22 | end 23 | 24 | -------------------------------------------------------------------------------- /test/macrotest.rb: -------------------------------------------------------------------------------- 1 | class Module 2 | def myattr(*x) 3 | str = "" 4 | x.each do |e| 5 | eval "def #{e}; @#{e}; end\n" 6 | end 7 | end 8 | end 9 | 10 | class Foo 11 | myattr :foo, :bar 12 | def initialize 13 | @foo = 1 14 | @bar = 3 15 | end 16 | def myattr(x) 17 | p x 18 | end 19 | end 20 | 21 | Foo.new.myattr("abc") 22 | p Foo.new.bar 23 | 24 | def fact(x) 25 | if x == 0 then 26 | 1 27 | else 28 | x * fact(x - 1) 29 | end 30 | end 31 | 32 | def fact_inline(x) 33 | eval "\" a = #{fact(x)} \"" 34 | end 35 | 36 | y= 5 37 | p fact_inline(5) 38 | 39 | =begin 40 | # Maybe this sample is impossible.... 41 | 42 | class Array 43 | def myeach(act) 44 | idx = "i" 45 | ed = "e" 46 | eval(" 47 | #{idx} = 0 48 | #{ed} = self.size 49 | while #{idx} < #{ed} 50 | #{act} self[#{idx}] 51 | #{idx} += 1 52 | end 53 | self 54 | ") 55 | end 56 | end 57 | 58 | p [1, 2, 3, 6].myeach("p ") {|e| p e} 59 | 60 | =end 61 | -------------------------------------------------------------------------------- /test/regexptest.rb: -------------------------------------------------------------------------------- 1 | p "OK" 2 | a = "3+" 3 | p /1/ =~ "1" 4 | p /11*/ =~ "1" 5 | p /11*/ =~ "111" 6 | p /11+/ =~ "111" 7 | p /13*/ =~ "1" 8 | p /13+/ =~ "13" 9 | p /13+/ =~ "1333" 10 | p /1#{a}/ =~ "1333" 11 | 12 | p "NG" 13 | p /1/ =~ "2" 14 | p /11*/ =~ "2" 15 | p /11*/ =~ "222" 16 | p /11+/ =~ "121" 17 | p /13*/ =~ "5" 18 | p /13+/ =~ "15" 19 | p /13+/ =~ "1253" 20 | p /1#{a}/ =~ "12534" 21 | 22 | -------------------------------------------------------------------------------- /test/requiretest.rb: -------------------------------------------------------------------------------- 1 | require 'basictest.rb' 2 | -------------------------------------------------------------------------------- /test/th_nested.rb: -------------------------------------------------------------------------------- 1 | class Foo 2 | def initialize 3 | @res = 0 4 | end 5 | 6 | attr_accessor :res 7 | 8 | def foo 9 | th = nil 10 | th2 = YTLJit::Runtime::Thread.new do 11 | th = YTLJit::Runtime::Thread.new do 12 | @res = 2 13 | end 14 | @res = 1 15 | end 16 | while th == nil 17 | end 18 | th.join 19 | p @res 20 | th2.join 21 | p @res 22 | end 23 | 24 | def self_merge(cself, pself) 25 | pself.res = pself.res + cself.res 26 | pself 27 | end 28 | end 29 | 30 | Foo.new.foo 31 | 32 | -------------------------------------------------------------------------------- /test/threadfib.rb: -------------------------------------------------------------------------------- 1 | # ytl -r runtime/thread.rb threadtest.rb 2 | def fib(x) 3 | if x < 2 then 4 | 1 5 | else 6 | fib(x - 1) + fib(x -2) 7 | end 8 | end 9 | 10 | class MultiFib 11 | def initialize 12 | @res = 32 # 32 is dummy (not 0 to detect bug) 13 | end 14 | attr_accessor :res 15 | 16 | def compute(n) 17 | th = YTLJit::Runtime::Thread.new(n) do |n2| 18 | @res = fib(n2 - 2) 19 | end 20 | 21 | @res = fib(n - 1) 22 | 23 | th.join 24 | @res 25 | end 26 | 27 | def self_merge(cself, pself) 28 | pself.res = pself.res + cself.res 29 | pself 30 | end 31 | end 32 | 33 | mfib = MultiFib.new 34 | print "computing fib 2 threads fib(40) \n" 35 | p mfib.compute(40) 36 | print "single fib(40)\n" 37 | p fib(40) 38 | -------------------------------------------------------------------------------- /test/threadtest.rb: -------------------------------------------------------------------------------- 1 | # ytl -r runtime/thread.rb threadtest.rb 2 | p "start" 3 | def fib(x) 4 | if x < 2 then 5 | 1 6 | else 7 | fib(x - 1) + fib(x -2) 8 | end 9 | end 10 | 11 | class Foo 12 | def initialize 13 | @res = 0 14 | end 15 | 16 | attr :res 17 | 18 | def foo 19 | YTLJit::Runtime::Thread.new do |arg| 20 | @res = fib(30) 21 | end 22 | end 23 | 24 | # Merge method whose return value is self object of joined thread 25 | #=begin 26 | def self_merge(cself, pself) 27 | cself 28 | end 29 | #=end 30 | 31 | end 32 | 33 | p self 34 | foo = Foo.new 35 | th = foo.foo 36 | p "computing fib 2 threads" 37 | p fib(30) 38 | p th 39 | 40 | th.join 41 | p foo.res 42 | -------------------------------------------------------------------------------- /ytl.gemspec: -------------------------------------------------------------------------------- 1 | spec = Gem::Specification.new do |s| 2 | s.platform = Gem::Platform::RUBY 3 | s.name = "ytl" 4 | s.version = "0.0.6" 5 | s.summary = "Very tiny subset of YARV to native code translator" 6 | s.authors = ["Hideki Miura"] 7 | s.files = [*Dir.glob("{lib}/*.rb"), 8 | *Dir.glob("{lib}/ytl/*.rb"), 9 | *Dir.glob("{runtime}/*.rb"), 10 | *Dir.glob("{test}/*.rb"), 11 | *Dir.glob("{bin}/ytl"), 12 | "README"] 13 | s.require_path = "lib" 14 | s.executables = ['ytl'] 15 | s.test_files = Dir.glob("{test}/*.rb") 16 | s.add_dependency('ytljit', [">= 0.0.10"]) 17 | end 18 | --------------------------------------------------------------------------------