ruby-llvm/ruby-llvm

View on GitHub
lib/llvm/core/value.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

module LLVM
  class Value
    include PointerIdentity

    # @private
    def self.from_ptr(ptr)
      return if ptr.null?
      val = allocate
      val.instance_variable_set(:@ptr, ptr)
      val
    end

    def self.from_ptr_kind(ptr)
      return if ptr.null?

      kind = C.get_value_kind(ptr)
      case kind
      when :instruction
        Instruction.from_ptr(ptr)
      when :const_int
        Int.from_ptr(ptr)
      when :const_fp
        Float.from_ptr(ptr)
      when :poison
        Poison.from_ptr(ptr)
      when :global_variable
        GlobalValue.from_ptr(ptr)
      else
        raise "from_ptr_kind cannot handle: #{kind}"
      end
    end

    # Returns the Value type. This is abstract and is overidden by its subclasses.
    def self.type
      raise NotImplementedError, "#{name}.type() is abstract."
    end

    def self.to_ptr
      type.to_ptr
    end

    # Returns the value's type.
    def type
      Type.from_ptr(C.type_of(self), nil)
    end

    # Returns the value's kind.
    def kind
      C.get_value_kind(self)
    end

    def allocated_type
      Type.from_ptr(C.get_allocated_type(self), nil)
    end

    # Returns the value's name.
    def name
      C.get_value_name(self)
    end

    # Sets the value's name to str.
    def name=(str)
      C.set_value_name(self, str)
      str
    end

    # Print the value's IR to stdout.
    def dump
      C.dump_value(self)
    end

    def to_s
      C.print_value_to_string(self)
    end

    # Returns whether the value is constant.
    def constant?
      case C.is_constant(self)
      when 0 then false
      when 1 then true
      end
    end

    # Returns whether the value is null.
    def null?
      case C.is_null(self)
      when 0 then false
      when 1 then true
      end
    end

    # Returns whether the value is undefined.
    def undefined?
      case C.is_undef(self)
      when 0 then false
      when 1 then true
      end
    end

    # Adds attr to this value's attributes.
    def add_attribute(attr)
      fun = param_parent
      return unless fun

      index = param_index(fun)
      return unless index

      fun.add_attribute(attr, index)
    end

    # Removes the given attribute from the function.
    def remove_attribute(attr)
      fun = param_parent
      return unless fun

      index = param_index(fun)
      return unless index

      fun.remove_attribute(attr, index)
    end

    # Get the parent module of a global variable, including functions
    def global_parent
      LLVM::Module.from_ptr(C.get_global_parent(self))
    end

    private

    # get function this param belongs to
    # return if it is not a param
    def param_parent
      valueref = C.get_param_parent(self)
      return if valueref.null?
      fun = LLVM::Function.from_ptr(valueref)
      return if fun.null?
      fun
    end

    # get index of this param by llvm count (+1 from parametercollection)
    def param_index(fun)
      index = fun.params.find_index(self)
      return if index.nil?
      index + 1
    end

  end

  class Argument < Value
  end

  class BasicBlock < Value
    # Creates a basic block for the given function with the given name.
    def self.create(fun = nil, name = "")
      from_ptr(C.append_basic_block(fun, name))
    end

    # Build the basic block with the given builder. Creates a new one if nil. Yields the builder.
    def build(builder = nil)
      if builder.nil?
        builder = Builder.new
        builder.position_at_end(self)
        yield builder
        builder.dispose
      else
        builder.position_at_end(self)
        yield builder
      end
    end

    # Returns the parent of this basic block (a Function).
    def parent
      fp = C.get_basic_block_parent(self)
      LLVM::Function.from_ptr(fp) unless fp.null?
    end

    # Returns the next basic block in the sequence.
    def next
      ptr = C.get_next_basic_block(self)
      BasicBlock.from_ptr(ptr) unless ptr.null?
    end

    # Returns the previous basic block in the sequence.
    def previous
      ptr = C.get_previous_basic_block(self)
      BasicBlock.from_ptr(ptr) unless ptr.null?
    end

    # deprecated
    def first_instruction
      instructions.first
    end

    # deprecated
    def last_instruction
      instructions.last
    end

    # Returns an Enumerable of the Instructions in the current block.
    def instructions
      @instructions ||= InstructionCollection.new(self)
    end

    # @private
    class InstructionCollection
      include Enumerable

      def initialize(block)
        @block = block
      end

      # Iterates through each Instruction in the collection.
      def each
        return to_enum :each unless block_given?
        inst, last = first, last

        while inst
          yield inst
          break if inst == last
          inst = inst.next
        end

        self
      end

      # Returns the first Instruction in the collection.
      def first
        ptr = C.get_first_instruction(@block)
        LLVM::Instruction.from_ptr(ptr) unless ptr.null?
      end

      # Returns the last Instruction in the collection.
      def last
        ptr = C.get_last_instruction(@block)
        LLVM::Instruction.from_ptr(ptr) unless ptr.null?
      end
    end
  end

  class User < Value
    # Returns an Enumerable of the operands in this user.
    def operands
      @operand_collection ||= OperandCollection.new(self)
    end

    # @private
    class OperandCollection
      include Enumerable

      def initialize(user)
        @user = user
      end

      # Get a reference to an operand by index.
      def [](i)
        ptr = C.get_operand(@user, i)
        Value.from_ptr(ptr) unless ptr.null?
      end

      # Set or replace an operand by index.
      def []=(i, v)
        C.set_operand(@user, i, v)
      end

      # Returns the number of operands in the collection.
      def size
        C.get_num_operands(@user)
      end

      # Iterates through each operand in the collection.
      def each
        return to_enum :each unless block_given?
        0.upto(size - 1) { |i| yield self[i] }
        self
      end
    end
  end

  class Constant < User
    # Creates a null constant of Type.
    def self.null(type)
      from_ptr(C.const_null(type))
    end

    # Creates a undefined constant of Type.
    def self.undef(type)
      from_ptr(C.get_undef(type))
    end

    # Creates a null pointer constant of Type.
    def self.null_ptr(type)
      from_ptr(C.const_pointer_null(type))
    end

    # Bitcast this constant to Type.
    def bitcast_to(type)
      ConstantExpr.from_ptr(C.const_bit_cast(self, type))
    end

    # @deprecated
    alias bit_cast bitcast_to

    # Returns the element pointer at the given indices of the constant.
    # For more information on gep go to: http://llvm.org/docs/GetElementPtr.html
    def gep(*indices)
      indices = Array(indices)
      FFI::MemoryPointer.new(FFI.type_size(:pointer) * indices.size) do |indices_ptr|
        indices_ptr.write_array_of_pointer(indices)
        return ConstantExpr.from_ptr(
          C.const_gep(self, indices_ptr, indices.size))
      end
    end

    # Conversion to integer.
    def ptr_to_int(type)
      ConstantInt.from_ptr(C.const_ptr_to_int(self, type))
    end
  end

  module Support
    def allocate_pointers(size_or_values, &block)
      if size_or_values.is_a?(Integer)
        raise ArgumentError, 'block not given' unless block
        size = size_or_values
        values = (0...size).map { |i| yield i }
      else
        values = size_or_values
        size = values.size
      end
      FFI::MemoryPointer.new(:pointer, size).write_array_of_pointer(values)
    end

    module_function :allocate_pointers
  end

  class ConstantArray < Constant
    def self.string(str, null_terminate = true)
      from_ptr(C.const_string(str, str.length, null_terminate ? 0 : 1))
    end

    # ConstantArray.const(type, 3) {|i| ... } or
    # ConstantArray.const(type, [...])
    def self.const(type, size_or_values, &block)
      vals = LLVM::Support.allocate_pointers(size_or_values, &block)
      from_ptr C.const_array(type, vals, vals.size / vals.type_size)
    end

    def size
      C.get_array_length(type)
    end

    def [](idx)
      self.class.from_ptr(C.get_aggregate_element(self, idx))
    end
  end

  class ConstantExpr < Constant
  end

  class ConstantInt < Constant
    def self.all_ones
      from_ptr(C.const_all_ones(type))
    end

    # Creates a ConstantInt from an integer.
    def self.from_i(n, signed = true)
      from_ptr(C.const_int(type, n, signed ? 1 : 0))
    end

    def self.parse(str, radix = 10)
      from_ptr(C.const_int_of_string(type, str, radix))
    end

    # Negation.
    def -@
      self.class.from_ptr(C.const_neg(self))
    end

    alias neg -@

    # "No signed wrap" negation.
    def nsw_neg
      self.class.from_ptr(C.const_nsw_neg(self))
    end

    # "No unsigned wrap" negation.
    def nuw_neg
      self.class.from_ptr(C.const_nuw_neg(self))
    end

    # Addition.
    def +(rhs)
      self.class.from_ptr(C.const_add(self, rhs))
    end

    alias add +

    # "No signed wrap" addition.
    def nsw_add(rhs)
      self.class.from_ptr(C.const_nsw_add(self, rhs))
    end

    # "No unsigned wrap" addition.
    def nuw_add(rhs)
      self.class.from_ptr(C.const_nuw_add(self, rhs))
    end

    # Subtraction.
    def -(rhs)
      self.class.from_ptr(C.const_sub(self, rhs))
    end

    alias sub -

    # "No signed wrap" subtraction.
    def nsw_sub(rhs)
      self.class.from_ptr(C.const_nsw_sub(self, rhs))
    end

    # "No unsigned wrap" subtraction.
    def nuw_sub(rhs)
      self.class.from_ptr(C.const_nuw_sub(self, rhs))
    end

    # Multiplication.
    def *(rhs)
      self.class.from_ptr(C.const_mul(self, rhs))
    end

    alias mul *

    # "No signed wrap" multiplication.
    def nsw_mul(rhs)
      self.class.from_ptr(C.const_nsw_mul(self, rhs))
    end

    # "No unsigned wrap" multiplication.
    def nuw_mul(rhs)
      self.class.from_ptr(C.const_nuw_mul(self, rhs))
    end

    # Unsigned division.
    def udiv(rhs)
      raise "constant udiv removed in LLVM 15"
    end

    # Signed division.
    def /(rhs)
      raise "constant sdiv removed in LLVM 15"
    end

    # Unsigned remainder.
    def urem(rhs)
      raise "constant urem removed in LLVM 15"
    end

    # Signed remainder.
    def rem(rhs)
      raise "constant srem removed in LLVM 15"
    end

    # Boolean negation.
    def ~@
      self.class.from_ptr(C.const_not(self))
    end

    alias not ~

    # Integer AND.
    def &(rhs)
      self.class.from_ptr(C.const_and(self, rhs))
    end

    alias and &

    # Integer OR.
    def |(rhs)
      self.class.from_ptr(C.const_or(self, rhs))
    end

    alias or |

    # Integer XOR.
    def ^(rhs)
      self.class.from_ptr(C.const_xor(self, rhs))
    end

    alias xor ^

    # Shift left.
    def <<(bits)
      self.class.from_ptr(C.const_shl(self, bits))
    end

    alias shl <<

    # Shift right.
    def >>(bits)
      self.class.from_ptr(C.const_l_shr(self, bits))
    end

    alias shr >>

    # Arithmatic shift right.
    def ashr(bits)
      self.class.from_ptr(C.const_a_shr(self, bits))
    end

    # Integer comparison using the predicate specified via the first parameter.
    # Predicate can be any of:
    #   :eq  - equal to
    #   :ne  - not equal to
    #   :ugt - unsigned greater than
    #   :uge - unsigned greater than or equal to
    #   :ult - unsigned less than
    #   :ule - unsigned less than or equal to
    #   :sgt - signed greater than
    #   :sge - signed greater than or equal to
    #   :slt - signed less than
    #   :sle - signed less than or equal to
    def icmp(pred, rhs)
      self.class.from_ptr(C.const_i_cmp(pred, self, rhs))
    end

    # Conversion to pointer.
    def int_to_ptr(type)
      ConstantExpr.from_ptr(C.const_int_to_ptr(self, type))
    end

    # constant zext
    def zext(type)
      self.class.from_ptr(C.const_z_ext(self, type))
    end

    # constant sext
    def sext(type)
      self.class.from_ptr(C.const_s_ext(self, type))
    end
    alias_method :ext, :sext

    # constant trunc
    def trunc(type)
      self.class.from_ptr(C.const_trunc(self, type))
    end

    # LLVMValueRef LLVMConstSIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
    def to_f(type)
      self.class.from_ptr(C.const_si_to_fp(self, type))
    end
  end

  def self.const_missing(const)
    case const.to_s
    when /Int(\d+)/
      width = Regexp.last_match(1).to_i
      name  = "Int#{width}"
      eval <<-KLASS
        class #{name} < ConstantInt
          def self.type
            Type.from_ptr(C.int_type(#{width}), :integer)
          end
        end
      KLASS
      const_get(name)
    else
      super
    end
  end

  # Native integer type
  bits = FFI.type_size(:int) * 8
  ::LLVM::Int = const_get(:"Int#{bits}")

  # Creates a LLVM Int (subclass of ConstantInt) at the NATIVE_INT_SIZE from a integer (val).
  def self.Int(val)
    case val
    when LLVM::ConstantInt then val
    when Integer then Int.from_i(val)
    when Value
      return val if val.type.kind == :integer
      raise "value not of integer type: #{val.type.kind}"
    else raise "can't make an LLVM::ConstantInt from #{val.class.name}"
    end
  end

  # Boolean values
  ::LLVM::TRUE = ::LLVM::Int1.from_i(-1)
  ::LLVM::FALSE = ::LLVM::Int1.from_i(0)

  class ConstantReal < Constant
    # Creates a ConstantReal from a float of Type.
    def self.from_f(n)
      from_ptr(C.const_real(type, n))
    end

    def self.parse(type, str)
      from_ptr(C.const_real_of_string(type, str))
    end

    # Negation.
    def -@
      raise "constant fneg removed in LLVM 16"
    end

    # Returns the result of adding this ConstantReal to rhs.
    def +(rhs)
      raise "constant fadd removed in LLVM 15"
    end

    # Returns the result of multiplying this ConstantReal by rhs.
    def *(rhs)
      raise "constant fmul removed in LLVM 15"
    end

    # Returns the result of dividing this ConstantReal by rhs.
    def /(rhs)
      raise "constant fdiv removed in LLVM 15"
    end

    # Remainder.
    def rem(rhs)
      raise "constant frem removed in LLVM 15"
    end

    # Floating point comparison using the predicate specified via the first
    # parameter. Predicate can be any of:
    #   :ord  - ordered
    #   :uno  - unordered: isnan(X) | isnan(Y)
    #   :oeq  - ordered and equal to
    #   :oeq  - unordered and equal to
    #   :one  - ordered and not equal to
    #   :one  - unordered and not equal to
    #   :ogt  - ordered and greater than
    #   :uge  - unordered and greater than or equal to
    #   :olt  - ordered and less than
    #   :ule  - unordered and less than or equal to
    #   :oge  - ordered and greater than or equal to
    #   :sge  - unordered and greater than or equal to
    #   :ole  - ordered and less than or equal to
    #   :sle  - unordered and less than or equal to
    #   :true - always true
    #   :false- always false
    def fcmp(pred, rhs)
      self.class.from_ptr(C.llmv_const_f_cmp(pred, self, rhs))
    end

    # constant FPToSI
    # LLVMValueRef LLVMConstFPToSI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
    def to_i(type)
      self.class.from_ptr(C.const_fp_to_si(self, type))
    end

    # Constant FPExt
    # this is a signed extension
    def ext(type)
      self.class.from_ptr(C.const_fp_ext(self, type))
    end
    alias_method :sext, :ext

    def trunc(type)
      self.class.from_ptr(C.const_fp_trunc(self, type))
    end

  end

  class Float < ConstantReal
    # Return a Type representation of the float.
    def self.type
      Type.from_ptr(C.float_type, :float)
    end
  end

  # Create a LLVM::Float from a Ruby Float (val).
  def self.Float(val)
    Float.from_f(val)
  end

  class Double < ConstantReal
    def self.type
      Type.from_ptr(C.double_type, :double)
    end
  end

  def self.Double(val)
    Double.from_f(val)
  end

  class ConstantStruct < Constant
    # ConstantStruct.const(size) {|i| ... } or
    # ConstantStruct.const([...])
    def self.const(size_or_values, packed = false, &block)
      vals = LLVM::Support.allocate_pointers(size_or_values, &block)
      from_ptr C.const_struct(vals, vals.size / vals.type_size, packed ? 1 : 0)
    end

    def self.named_const(type, size_or_values, &block)
      vals = LLVM::Support.allocate_pointers(size_or_values, &block)
      from_ptr C.const_named_struct(type, vals, vals.size / vals.type_size)
    end

    def [](idx)
      self.class.from_ptr(C.get_aggregate_element(self, idx))
    end
  end

  class ConstantVector < Constant
    def self.all_ones
      from_ptr(C.const_all_ones(type))
    end

    def self.const(size_or_values, &block)
      vals = LLVM::Support.allocate_pointers(size_or_values, &block)
      from_ptr(C.const_vector(vals, vals.size / vals.type_size))
    end

    def size
      C.get_vector_size(type)
    end

    def [](idx)
      self.class.from_ptr(C.get_aggregate_element(self, idx))
    end
  end

  class GlobalValue < Constant
    def declaration?
      C.is_declaration(self)
    end

    def linkage
      C.get_linkage(self)
    end

    def linkage=(linkage)
      C.set_linkage(self, linkage)
    end

    def section
      C.get_section(self)
    end

    def section=(section)
      C.set_section(self, section)
    end

    def visibility
      C.get_visibility(self)
    end

    def visibility=(viz)
      C.set_visibility(self, viz)
    end

    def alignment
      C.get_alignment(self)
    end

    def alignment=(bytes)
      C.set_alignment(self, bytes)
    end

    def initializer
      Value.from_ptr(C.get_initializer(self))
    end

    def initializer=(val)
      C.set_initializer(self, val)
    end

    def global_constant?
      C.is_global_constant(self) != 0
    end

    def global_constant=(flag)
      if flag.kind_of?(Integer)
        warn 'Warning: Passing Integer value to LLVM::GlobalValue#global_constant=(Boolean) is deprecated.'
        flag = !flag.zero?
      end

      C.set_global_constant(self, flag ? 1 : 0)
    end

    def unnamed_addr?
      C.has_unnamed_addr(self) != 0
    end

    def unnamed_addr=(flag)
      C.set_unnamed_addr(self, flag ? 1 : 0)
    end

    def dll_storage_class
      C.get_dll_storage_class(self)
    end

    def dll_storage_class=(klass)
      C.set_dll_storage_class(self, klass)
    end
  end

  class Function < GlobalValue
    # Sets the function's calling convention and returns it.
    def call_conv=(conv)
      C.set_function_call_conv(self, conv)
      conv
    end

    # gets the calling convention of the function
    def call_conv
      C.get_function_call_conv(self)
    end

    # Returns an Enumerable of the BasicBlocks in this function.
    def basic_blocks
      @basic_block_collection ||= BasicBlockCollection.new(self)
    end

    def function_type
      Type.from_ptr(C.get_element_type(self), :function)
    end

    # In LLVM 15, not overriding this yields a pointer type instead of a function type
    def type
      function_type
    end

    def return_type
      type.return_type
    end

    # Adds attr to this value's attributes.
    def add_attribute(attr, index = -1)
      AttributeCollection.new(self, index).add(attr)
    end

    # Removes the given attribute from the function.
    def remove_attribute(attr, index = -1)
      AttributeCollection.new(self, index).remove(attr)
    end

    def attribute_count
      function_attributes.count
    end

    def attributes
      function_attributes.to_a
    end

    def readnone?
      attributes.detect(&:readnone?)
    end

    def readonly?
      attributes.detect(&:readonly?)
    end

    def writeonly?
      attributes.detect(&:writeonly?)
    end

    def function_attributes
      AttributeCollection.new(self, -1)
    end

    def return_attributes
      AttributeCollection.new(self, 0)
    end

    def param_attributes(index)
      AttributeCollection.new(self, index)
    end

    class AttributeCollection

      def initialize(fun, index)
        @fun = fun
        @index = index
      end

      def add(attr)
        attr_ref = case attr
        when Attribute
          attr
        when Symbol, String
          attr_kind_id = attribute_id(attr)
          if attr_kind_id.is_a?(Integer)
            ctx = Context.global
            C.create_enum_attribute(ctx, attr_kind_id, 0)
          else
            attr_kind_id
          end
        else
          raise "Adding unknown attribute type: #{attr.inspect}"
        end
        C.add_attribute_at_index(@fun, @index, attr_ref)
      end

      def remove(attr)
        attr_kind_id = attribute_id(attr)
        C.remove_enum_attribute_at_index(@fun, @index, attr_kind_id)
      end

      def count
        C.get_attribute_count_at_index(@fun, @index)
      end

      def to_a
        attr_refs = nil
        n = count
        FFI::MemoryPointer.new(:pointer, n) do |p|
          C.get_attributes_at_index(@fun, @index, p)
          attr_refs = p.read_array_of_type(:pointer, :read_pointer, n)
        end

        attr_refs.map { |e| Attribute.send(:from_ptr, e) }
      end

      private

      def attribute_name(attr_name)
        attr_name = attr_name.to_s
        if /_attribute$/.match?(attr_name)
          attr_name.chomp('_attribute').tr('_', '')
        else
          attr_name
        end
      end

      def attribute_id(attr_name)
        upgrade = upgrade_attr(attr_name)
        return upgrade if upgrade

        attr_mem = FFI::MemoryPointer.from_string(attribute_name(attr_name))
        attr_kind_id = C.get_enum_attribute_kind_for_name(attr_mem, attr_mem.size - 1)

        raise "No attribute named: #{attr_name}" if attr_kind_id.zero?
        attr_kind_id
      end

      # Upgrade attributes from before LLVM 16
      # readnone, readonly, writeonly
      def upgrade_attr(attr)
        case attr.to_sym
        when :readnone
          LLVM::Attribute.memory
        when :readonly
          LLVM::Attribute.memory(memory: :read)
        when :writeonly
          LLVM::Attribute.memory(memory: :write)
        when :inaccessiblememonly
          LLVM::Attribute.memory(inaccessiblemem: :readwrite)
        end
      end

    end

    # @private
    class BasicBlockCollection
      include Enumerable

      def initialize(fun)
        @fun = fun
      end

      # Returns the number of BasicBlocks in the collection.
      def size
        C.count_basic_blocks(@fun)
      end

      # Iterates through each BasicBlock in the collection.
      def each
        return to_enum :each unless block_given?

        ptr = C.get_first_basic_block(@fun)
        0.upto(size - 1) do |i|
          yield BasicBlock.from_ptr(ptr)
          ptr = C.get_next_basic_block(ptr)
        end

        self
      end

      # Adds a BasicBlock with the given name to the end of the collection.
      def append(name = "")
        BasicBlock.create(@fun, name)
      end

      # Returns the entry BasicBlock in the collection. This is the block the
      # function starts on.
      def entry
        BasicBlock.from_ptr(C.get_entry_basic_block(@fun))
      end

      # Returns the first BasicBlock in the collection.
      def first
        ptr = C.get_first_basic_block(@fun)
        BasicBlock.from_ptr(ptr) unless ptr.null?
      end

      # Returns the last BasicBlock in the collection.
      def last
        ptr = C.get_last_basic_block(@fun)
        BasicBlock.from_ptr(ptr) unless ptr.null?
      end
    end

    # Returns an Enumerable of the parameters in the function.
    def params
      @parameter_collection ||= ParameterCollection.new(self)
    end

    # @private
    class ParameterCollection
      def initialize(fun)
        @fun = fun
      end

      # Returns a Value representation of the parameter at the given index.
      def [](i)
        sz = size
        i = sz + i if i < 0
        return unless 0 <= i && i < sz
        Value.from_ptr(C.get_param(@fun, i))
      end

      # Returns the number of paramters in the collection.
      def size
        C.count_params(@fun)
      end

      include Enumerable

      # Iteraters through each parameter in the collection.
      def each
        return to_enum :each unless block_given?
        0.upto(size - 1) { |i| yield self[i] }
        self
      end
    end

    def gc=(name)
      C.set_gc(self, name)
    end

    def gc
      C.get_gc(self)
    end

    def inspect
      {
        signature: to_s.lines[attribute_count == 0 ? 0 : 1],
        type: type.to_s,
        attributes: attribute_count,
        valid: valid?,
        blocks: basic_blocks.size,
        lines: to_s.lines.size,
      }.to_s
    end
  end

  class GlobalAlias < GlobalValue
  end

  class GlobalVariable < GlobalValue
    def initializer
      Value.from_ptr(C.get_initializer(self))
    end

    def initializer=(val)
      C.set_initializer(self, val)
    end

    def thread_local?
      case C.is_thread_local(self)
      when 0 then false
      else true
      end
    end

    def thread_local=(local)
      C.set_thread_local(self, local ? 1 : 0)
    end
  end

  class Instruction < User

    def self.from_ptr(ptr)
      kind = C.get_value_kind(ptr)
      kind == :instruction ? super : LLVM::Value.from_ptr_kind(ptr)
    end

    # Returns the parent of the instruction (a BasicBlock).
    def parent
      ptr = C.get_instruction_parent(self)
      LLVM::BasicBlock.from_ptr(ptr) unless ptr.null?
    end

    # Returns the next instruction after this one.
    def next
      ptr = C.get_next_instruction(self)
      LLVM::Instruction.from_ptr(ptr) unless ptr.null?
    end

    # Returns the previous instruction before this one.
    def previous
      ptr = C.get_previous_instruction(self)
      LLVM::Instruction.from_ptr(ptr) unless ptr.null?
    end

    def opcode
      C.get_instruction_opcode(self)
    end

    def inspect
      { self.class.name => { opcode: opcode, ptr: @ptr } }.to_s
    end
  end

  class Poison < Value

  end

  class CallInst < Instruction
    # Sets the call convention to conv.
    def call_conv=(conv)
      C.set_instruction_call_conv(self, conv)
      conv
    end

    # Returns the call insatnce's call convention.
    def call_conv
      C.get_instruction_call_conv(self)
    end
  end

  # @private
  class Phi < Instruction
    # Add incoming branches to a phi node by passing an alternating list of
    # resulting values and BasicBlocks. e.g.
    #   phi.add_incoming(val1, block1, val2, block2, ...)
    def add_incoming(incoming)
      blks = incoming.keys
      vals = incoming.values_at(*blks)
      size = incoming.size

      FFI::MemoryPointer.new(FFI.type_size(:pointer) * size) do |vals_ptr|
        vals_ptr.write_array_of_pointer(vals)
        FFI::MemoryPointer.new(FFI.type_size(:pointer) * size) do |blks_ptr|
          blks_ptr.write_array_of_pointer(blks)
          C.add_incoming(self, vals_ptr, blks_ptr, vals.size)
        end
      end

      nil
    end
  end

  # @private
  class SwitchInst < Instruction
    # Adds a case to a switch instruction. First the value to match on, then
    # the basic block.
    def add_case(val, block)
      C.add_case(self, val, block)
    end
  end


  # @private
  class IndirectBr < Instruction
    # Adds a basic block reference as a destination for this indirect branch.
    def add_dest(dest)
      C.add_destination(self, dest)
    end

    alias :<< :add_dest
  end
end