The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ruby <<END

V = :value
L = :label_ptr
B = :label
N = :nint
T = :type_t
_ = :void

def incoming_args(arg_types)
  num_args = arg_types.length
  return (1..num_args).map { |n| ", VALUE arg#{n}" }.join
end

def declaration(name, type)
  case type
  when V then return "jit_value_t #{name};"
  when L then return "jit_label_t * #{name};"
  when B then return "jit_label_t * #{name};"
  when N then return "jit_nint #{name};"
  when T then return "jit_type_t #{name};"
  when :void then nil
  else raise "Invalid type #{type}"
  end
end

def write_declaration(name, type, indent)
  d = declaration(name, type)
  if d then
    puts "#{' '*indent}#{d}"
  end
end

def ruby_type(type)
  case type
  when V then return "rb_cValue"
  when L then return "rb_cLabel"
  when B then return "rb_cLabel"
  when N then return "rb_cFixnum"
  when T then return "rb_cType"
  else raise "Invalid type #{type}"
  end
end

def jit_type(type)
  case type
  when V then return "struct _jit_value"
  when L then return "jit_label_t"
  when B then return "jit_label_t"
  when T then return "struct _jit_type"
  else raise "Invalid type #{type}"
  end
end

def get_value(type, arg, j_arg)
  case type
  when N then
    return "#{j_arg} = NUM2INT(#{arg})"
  else
    return "Data_Get_Struct(#{arg}, #{jit_type(type)}, #{j_arg})"
  end
end

def jit_insn_args(arg_types)
  num_args = arg_types.length
  arg_list = (1..num_args).map do |n|
    case arg_types[n-1]
    when B then ", *j_arg#{n}"
    else ", j_arg#{n}"
    end
  end
  return arg_list.join
end

def write_insn(name, retval_type, arg_types)
  num_args = arg_types.length
  an = [?a, ?e, ?i, ?o, ?u].include?(name[0])
  puts "/* Generate a#{an} #{name} instruction (see the libjit documentation for more"
  puts " * details. */"
  puts "static VALUE function_insn_#{name}(VALUE self#{incoming_args(arg_types)})"
  puts "{"
  puts "  jit_function_t function;"
  write_declaration('retval', retval_type, 2)
  arg_types.each_with_index do |type, n|
    write_declaration("j_arg#{n+1}", type, 2)
  end
  puts
  arg_types.each_with_index do |type, n|
    puts "  check_type(\"arg#{n+1}\", #{ruby_type(type)}, arg#{n+1});"
  end
  puts
  puts "  Data_Get_Struct(self, struct _jit_function, function);"
  arg_types.each_with_index do |type, n|
    puts "  " + get_value(type, "arg#{n+1}", "j_arg#{n+1}") + ";"
  end
  puts
  if retval_type == V then
    puts "  retval = jit_insn_#{name}(function#{jit_insn_args(arg_types)});"
    puts "  return Data_Wrap_Struct(rb_cValue, 0, 0, retval);"
  elsif retval_type == :void then
    puts "  jit_insn_#{name}(function#{jit_insn_args(arg_types)});"
    puts "  return Qnil;"
  else
    raise "Invalid retval type #{retval_type}"
  end
  puts "}"
end

insns = [
  [ 'load'                 , V, V       ] ,
  [ 'store'                , _, V, V    ] ,
  [ 'dup'                  , V, V       ] ,
  [ 'add'                  , V, V, V    ] , 
  [ 'add_ovf'              , V, V, V    ] , 
  [ 'sub'                  , V, V, V    ] , 
  [ 'sub_ovf'              , V, V, V    ] , 
  [ 'mul'                  , V, V, V    ] , 
  [ 'mul_ovf'              , V, V, V    ] , 
  [ 'div'                  , V, V, V    ] , 
  [ 'rem'                  , V, V, V    ] , 
  [ 'rem_ieee'             , V, V, V    ] , 
  [ 'neg'                  , V, V       ] , 
  [ 'and'                  , V, V, V    ] , 
  [ 'or'                   , V, V, V    ] , 
  [ 'xor'                  , V, V, V    ] , 
  [ 'not'                  , V, V       ] , 
  [ 'shl'                  , V, V, V    ] , 
  [ 'shr'                  , V, V, V    ] , 
  [ 'ushr'                 , V, V, V    ] , 
  [ 'sshr'                 , V, V, V    ] , 
  [ 'eq'                   , V, V, V    ] , 
  [ 'ne'                   , V, V, V    ] , 
  [ 'lt'                   , V, V, V    ] , 
  [ 'le'                   , V, V, V    ] , 
  [ 'gt'                   , V, V, V    ] , 
  [ 'ge'                   , V, V, V    ] , 
  [ 'cmpl'                 , V, V, V    ] , 
  [ 'cmpg'                 , V, V, V    ] , 
  [ 'to_bool'              , V, V       ] , 
  [ 'to_not_bool'          , V, V       ] , 
  [ 'acos'                 , V, V       ] , 
  [ 'asin'                 , V, V       ] , 
  [ 'atan'                 , V, V       ] , 
  [ 'atan2'                , V, V, V    ] ,
  [ 'ceil'                 , V, V       ] , 
  [ 'cos'                  , V, V       ] , 
  [ 'cosh'                 , V, V       ] , 
  [ 'exp'                  , V, V       ] , 
  [ 'floor'                , V, V       ] , 
  [ 'log'                  , V, V       ] , 
  [ 'log10'                , V, V       ] , 
  [ 'pow'                  , V, V, V    ] ,
  [ 'rint'                 , V, V       ] , 
  [ 'round'                , V, V       ] , 
  [ 'sin'                  , V, V       ] , 
  [ 'sinh'                 , V, V       ] , 
  [ 'sqrt'                 , V, V       ] , 
  [ 'tan'                  , V, V       ] , 
  [ 'tanh'                 , V, V       ] , 
  [ 'is_nan'               , V, V       ] , 
  [ 'is_finite'            , V, V       ] , 
  [ 'is_inf'               , V, V       ] , 
  [ 'abs'                  , V, V       ] , 
  [ 'min'                  , V, V, V    ] , 
  [ 'max'                  , V, V, V    ] , 
  [ 'sign'                 , V, V       ] , 
  [ 'branch'               , _, L       ] ,
  [ 'branch_if'            , _, V, L    ] ,
  [ 'branch_if_not'        , _, V, L    ] ,
  [ 'label'                , _, L       ] ,
  [ 'address_of'           , V, V       ] ,
  [ 'address_of_label'     , V, L       ] ,
  [ 'store_relative'       , _, V, N, V ] ,
  [ 'load_relative'        , V, V, N, T ] ,
  [ 'add_relative'         , V ,V, N    ] ,
  [ 'memcpy'               , _, V, V, V ] ,
  [ 'memmove'              , _, V, V, V ] ,
  [ 'memset'               , _, V, V, V ] ,
  [ 'alloca'               , V, V       ] ,
  [ 'load_elem'            , V, V, V, T ] ,
  [ 'store_elem'           , _, V, V, V ] ,
  [ 'move_blocks_to_end'   , _, B, B    ] ,
  [ 'move_blocks_to_start' , _, B, B    ] ,
  [ 'push'                 , _, V       ] ,
  [ 'push_ptr'             , _, V, T    ] ,
  [ 'pop_stack'            , _, N       ] ,
]

insns.each do |name, retval_type, *arg_types|
  write_insn(name, retval_type, arg_types)
  puts
end

puts "static void init_insns()"
puts "{"
insns.each do |name, retval_type, *arg_types|
  num_args = arg_types.length
  puts "  rb_define_method(rb_cFunction, \"insn_#{name}\", function_insn_#{name}, #{num_args});"
end
puts "}"

END