dottosym(x)::Symbol = x

function dottosym(x::Expr)::Symbol
  s1 = dottosym(x.args[1])
  xa2 = x.args[2]
  xa2 isa QuoteNode ? Symbol(s1, "###extractarray###", xa2.value) :
  Symbol(s1, "###extractarray###", xa2)
end
function extract_array_symbol_from_ref!(
  ls::LoopSet,
  ex::Expr,
  offset1::Int
)::Symbol
  ar = ex.args[1+offset1]
  if isa(ar, Symbol)
    return ar
  elseif isa(ar, Expr) && (ar.head === :(.) || ar.head === :ref)
    s = dottosym(ar)
    # pushpreamble!(ls, Expr(:(=), s, ar))
    pushprepreamble!(ls, Expr(:(=), s, ar))
    return s
  else
    throw("Indexing into the following expression was not recognized: $ar")
  end
end

function ref_from_expr!(ls, ex, offset1::Int, offset2::Int)
  ar = extract_array_symbol_from_ref!(ls, ex, offset1)
  ar, @view(ex.args[2+offset2:end])
end
ref_from_ref!(ls::LoopSet, ex::Expr) = ref_from_expr!(ls, ex, 0, 0)
ref_from_getindex!(ls::LoopSet, ex::Expr) = ref_from_expr!(ls, ex, 1, 1)
ref_from_setindex!(ls::LoopSet, ex::Expr) = ref_from_expr!(ls, ex, 1, 2)
function ref_from_expr!(ls::LoopSet, ex::Expr)
  if ex.head === :ref
    ref_from_ref!(ls, ex)
  else#if ex.head === :call
    f = first(ex.args)::Symbol
    f === :getindex ? ref_from_getindex!(ls, ex) : ref_from_setindex!(ls, ex)
  end
end

add_vptr!(ls::LoopSet, op::Operation) = add_vptr!(ls, op.ref)
add_vptr!(ls::LoopSet, mref::ArrayReferenceMeta) =
  add_vptr!(ls, mref.ref.array, vptr(mref))
# using VectorizationBase: noaliasstridedpointer
presbufsym(array) = Symbol('#', array, "#preserve#buffer#")
function add_vptr!(
  ls::LoopSet,
  array::Symbol,
  vptrarray::Symbol,
  actualarray::Bool = true
)
  if !includesarray(ls, array)
    push!(ls.includedarrays, array)
    actualarray && push!(ls.includedactualarrays, array)
    pushpreamble!(
      ls,
      Expr(
        :(=),
        Expr(:tuple, vptrarray, presbufsym(array)),
        Expr(:call, lv(:stridedpointer_preserve), array)
      )
    )
  end
  nothing
end

@inline staticdims(::Any) = One()
@inline staticdims(::CartesianIndices{N}) where {N} = StaticInt{N}()

function append_loop_staticdims!(valcall::Expr, loop::Loop, constoffset::Int)
  isstaticloop(loop) && return constoffset + 1
  push!(valcall.args, Expr(:call, lv(:staticdims), loop.rangesym))
  constoffset
end
function subset_vptr!(
  ls::LoopSet,
  vptr::Symbol,
  indnum::Int,
  ind,
  previndices,
  loopindex,
  D::Int
)
  subset = D == 0
  str_typ = subset ? "subset" : "index"
  subsetvptr = Symbol(vptr, "_##$(str_typ)##_$(indnum)_##with##_$(ind)_##")
  valcall = Expr(:call, +)
  constoffset = 1
  if indnum > 1
    offset = first(previndices) === DISCONTIGUOUS
    # valcall =  Expr(:call, :(+), valcall)
    for i ∈ 1:indnum-1
      if loopindex[i]
        loopdep = previndices[i+offset]
        if loopdep === CONSTANTZEROINDEX
          constoffset += 1
          continue
        end
      else
        # assumes all staticdims will be of equal length once expanded...
        # A[I + J, constindex], I and J may be CartesianIndices. This requires they all be of same number of dims
        loopdep = first(loopdependencies(ls.opdict[previndices[i+offset]]))
      end
      constoffset =
        append_loop_staticdims!(valcall, getloop(ls, loopdep), constoffset)
    end
  end
  # indm1 = ind isa Integer ? ind - 1 : Expr(:call, :-, ind, 1)
  f = lv(Core.ifelse(subset, :subsetview, :_gesp))
  constoffsetexpr = staticexpr(constoffset)
  if length(valcall.args) ≠ 1
    push!(valcall.args, constoffsetexpr)
    constoffsetexpr = valcall
  end
  fexpr = Expr(:call, f, vptr, constoffsetexpr, ind)
  subset || push!(fexpr.args, staticexpr(D))
  pushpreamble!(ls, Expr(:(=), subsetvptr, fexpr))
  subsetvptr
end

function gesp_const_offset!(
  ls::LoopSet,
  vptrarray::Symbol,
  ninds::Int,
  indices::Vector{Symbol},
  loopedindex::Vector{Bool},
  mlt::Integer,
  sym,
  D::Int
)
  if isone(mlt)
    subset_vptr!(ls, vptrarray, ninds, sym, indices, loopedindex, D)
  else
    # want same name for arrays to be given the same name if possible
    mltsym = Symbol(sym, "##multiplied##by##", mlt)
    pushpreamble!(ls, Expr(:(=), mltsym, Expr(:call, :(*), mlt, sym)))
    # pushprepreamble!(ls, Expr(:(=), mltsym, Expr(:call, :(*), mlt, sym)))
    subset_vptr!(ls, vptrarray, ninds, mltsym, indices, loopedindex, D)
  end
end
function gesp_const_offsets!(
  ls::LoopSet,
  vptrarray::Symbol,
  ninds::Int,
  indices::Vector{Symbol},
  loopedindex::Vector{Bool},
  mltsyms::Vector{Tuple{Int,Symbol}},
  D::Int
)
  length(mltsyms) > 1 && sort!(mltsyms; by = last) # if multiple have same combination of syms, make sure they match even if order is different
  for (mlt, sym) ∈ mltsyms
    vptrarray = gesp_const_offset!(
      ls,
      vptrarray,
      ninds,
      indices,
      loopedindex,
      mlt,
      sym,
      D
    )
  end
  vptrarray
end

byterepresentable(x)::Bool = false
byterepresentable(x::Integer)::Bool = typemin(Int8) ≤ x ≤ typemax(Int8)
function _addoffset!(
  indices,
  offsets,
  strides,
  loopedindex,
  loopdependencies,
  ind,
  offset,
  stride
)
  push!(indices, ind)
  push!(offsets, offset % Int8)
  # push!(offsets, (offset+stride-1) % Int8)
  push!(strides, stride % Int8)
  push!(loopedindex, true)
  push!(loopdependencies, ind)
  true
end

function addconstindex!(indices, offsets, strides, loopedindex, offset)
  push!(indices, CONSTANTZEROINDEX)
  # push!(offsets, (offset-1) % Int8)
  push!(offsets, (offset) % Int8)
  push!(strides, zero(Int8))
  push!(loopedindex, true)
  true
end
function addopindex!(
  parents::Vector{Operation},
  loopdependencies::Vector{Symbol},
  reduceddeps::Vector{Symbol},
  indices::Vector{Symbol},
  offsets::Vector{Int8},
  strides::Vector{Int8},
  loopedindex::Vector{Bool},
  indop::Operation,
  stride = one(Int8),
  offset = zero(Int8)
)
  pushparent!(parents, loopdependencies, reduceddeps, indop)
  push!(indices, name(indop))
  push!(offsets, offset)
  push!(strides, stride)
  push!(loopedindex, false)
  nothing
end

####################################################################################################
###################################### Index Parsing Helpers #######################################
####################################################################################################

function add_affine_index_expr!(
  ls::LoopSet,
  mult_syms::Vector{Tuple{Int,Symbol}},
  constant::Base.RefValue{Int},
  stride::Int,
  expr::Symbol
)
  push!(mult_syms, (stride, expr))
  return nothing
end
function add_affine_index_expr!(
  ls::LoopSet,
  mult_syms::Vector{Tuple{Int,Symbol}},
  constant::Base.RefValue{Int},
  stride::Int,
  expr::Integer
)
  constant[] += stride * expr
  return nothing
end
function add_affine_op!(
  ls::LoopSet,
  mult_syms::Vector{Tuple{Int,Symbol}},
  constant::Base.RefValue{Int},
  stride::Int,
  expr::Expr
)
  parent = add_operation!(
    ls,
    gensym!(ls, "indexpr"),
    expr,
    sizeof(Int),
    length(ls.loopsymbols)
  )
  add_affine_index_expr!(ls, mult_syms, constant, stride, name(parent))
  return nothing
end
function add_mul!(
  ls::LoopSet,
  mult_syms::Vector{Tuple{Int,Symbol}},
  constant::Base.RefValue{Int},
  stride::Int,
  arg1,
  arg2,
  expr
)
  if arg1 isa Integer
    add_affine_index_expr!(ls, mult_syms, constant, stride * arg1, arg2)
  elseif arg2 isa Integer
    add_affine_index_expr!(ls, mult_syms, constant, stride * arg2, arg1)
  else
    add_affine_op!(ls, mult_syms, constant, stride, expr)
  end
  return nothing
end
function add_affine_index_expr!(
  ls::LoopSet,
  mult_syms::Vector{Tuple{Int,Symbol}},
  constant::Base.RefValue{Int},
  stride::Int,
  expr::Expr
)
  expr.head === :call ||
    return add_affine_op!(ls, mult_syms, constant, stride, expr)
  f = expr.args[1]
  if f === :(*)
    @assert length(expr.args) == 3
    add_mul!(ls, mult_syms, constant, stride, expr.args[2], expr.args[3], expr)
  elseif f === :(-)
    if length(expr.args) == 3
      add_affine_index_expr!(ls, mult_syms, constant, stride, expr.args[2])
    elseif length(expr.args) ≠ 2
      throw("Subtraction expression: $expr has an invalid number of arguments.")
    end
    add_affine_index_expr!(ls, mult_syms, constant, -stride, last(expr.args))
  elseif f === :(+)
    for i ∈ 2:length(expr.args)
      add_affine_index_expr!(ls, mult_syms, constant, stride, expr.args[i])
    end
  else
    add_affine_op!(ls, mult_syms, constant, stride, expr)
  end
  return nothing
end
function affine_index_expression(
  ls::LoopSet,
  expr
)::Tuple{Int,Vector{Tuple{Int,Symbol}}}
  mult_syms = Tuple{Int,Symbol}[]
  constant = Ref(0)
  add_affine_index_expr!(ls, mult_syms, constant, 1, expr)
  return constant[], mult_syms
end

function muladd_index!(
  ls::LoopSet,
  parents,
  loopdependencies,
  reduceddeps,
  indices,
  offsets,
  strides,
  loopedindex,
  mlt::Int,
  sym::Symbol,
  offset::Int
)
  muladd_index!(
    ls,
    parents,
    loopdependencies,
    reduceddeps,
    indices,
    offsets,
    strides,
    loopedindex,
    mlt,
    getop(ls, sym, sizeof(Int)),
    offset
  )
end
function muladd_op!(ls::LoopSet, mlt::Int, sym::Symbol, offset::Int)
  muladd_op!(ls, mlt, getop(ls, sym, sizeof(Int)), offset)
end
function muladd_op!(ls::LoopSet, mlt::Int, symop::Operation, offset::Int)
  indsym = gensym!(ls, "affindexop")
  vparents = [symop]
  # vparents = [, mltop, offop]
  if mlt == -1
    f = :vsub_nsw
    if offset ≠ 0
      pushfirst!(vparents, add_constant!(ls, offset, sizeof(Int)))
    end
  elseif mlt == 1
    offset == 0 && return only(vparents)
    push!(vparents, add_constant!(ls, offset, sizeof(Int)))
    f = :vadd_nsw
  else
    push!(vparents, add_constant!(ls, mlt, sizeof(Int)))
    f = if offset == 0
      :vmul_nsw
    else
      push!(vparents, add_constant!(ls, offset, sizeof(Int)))
      :muladd
    end
  end
  add_compute!(ls, indsym, instruction(f), vparents, sizeof(Int))
end
function muladd_index!(
  ls::LoopSet,
  parents,
  loopdependencies,
  reduceddeps,
  indices,
  offsets,
  strides,
  loopedindex,
  mlt::Int,
  symop::Operation,
  offset::Int
)
  if byterepresentable(offset) & byterepresentable(mlt)
    addopindex!(
      parents,
      loopdependencies,
      reduceddeps,
      indices,
      offsets,
      strides,
      loopedindex,
      symop,
      mlt,
      offset
    )
  else
    indop = muladd_op!(ls, mlt, symop, offset)
    addopindex!(
      parents,
      loopdependencies,
      reduceddeps,
      indices,
      offsets,
      strides,
      loopedindex,
      indop
    )
  end
end

function getintconstant(ls::LoopSet, op::Operation)
  for (i, (v, bits, bool)) ∈ ls.preamble_symint
    i == identifier(op) || continue
    return byterepresentable(v), v % Int8
  end
  return false, zero(Int8)
end

function add_additive_index!(
  ls::LoopSet,
  opparents,
  vptrarray,
  sym,
  mop,
  ninds,
  indices,
  offsets,
  strides,
  loopedindex,
  loopdependencies,
  reduceddeps,
  offset,
  mlt,
  D
)
  factor = Core.ifelse((instruction(mop).instr === :sub_fast), -1, 1)
  if length(parents(mop)) == 2
    sub1 = parents(mop)[1]
    sub2 = parents(mop)[2]
    if isloopvalue(sub1) & isconstant(sub2)
      isliteralint, literalval = getintconstant(ls, sub2)
      if isliteralint
        _addoffset!(
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          name(sub1),
          offset + literalval,
          mlt
        )
      else
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          factor * mlt,
          name(sub2),
          D
        )
        _addoffset!(
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          name(sub1),
          offset,
          mlt
        )
      end
    elseif isloopvalue(sub2) & isconstant(sub1)
      isliteralint, literalval = getintconstant(ls, sub1)
      if isliteralint
        _addoffset!(
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          name(sub2),
          offset + literalval,
          factor * mlt
        )
      else
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          mlt,
          name(sub1),
          D
        )
        _addoffset!(
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          name(sub2),
          offset,
          factor * mlt
        )
      end
    else
      muladd_index!(
        ls,
        opparents,
        loopdependencies,
        reduceddeps,
        indices,
        offsets,
        strides,
        loopedindex,
        mlt,
        sym,
        offset
      )
    end
  else
    muladd_index!(
      ls,
      opparents,
      loopdependencies,
      reduceddeps,
      indices,
      offsets,
      strides,
      loopedindex,
      mlt,
      sym,
      offset
    )
  end
  vptrarray
end

function checkforoffset!(
  ls::LoopSet,
  vptrarray::Symbol,
  ninds::Int,
  opparents::Vector{Operation},
  indices::Vector{Symbol},
  offsets::Vector{Int8},
  strides::Vector{Int8},
  loopedindex::Vector{Bool},
  loopdependencies::Vector{Symbol},
  reduceddeps::Vector{Symbol},
  ind::Expr,
  D::Int
)::Symbol
  offset, mult_syms = affine_index_expression(ls, ind)
  let deleted = 0, N = length(mult_syms)
    for n ∈ 1:N
      ntemp = n - deleted
      mlt, sym = mult_syms[ntemp]
      opm = get(ls.opdict, sym, nothing)
      opm === nothing && continue
      isconstant(opm) || continue
      found = false
      for (opid, (intval, intsz, signed)) ∈ ls.preamble_symint
        if opid == identifier(opm)
          offset += intval * mlt
          deleted += 1
          deleteat!(mult_syms, ntemp)
          found = true
          break
        end
      end
      found && continue
      for (opid, nt) ∈ ls.preamble_zeros
        if opid == identifier(opm)
          deleted += 1
          deleteat!(mult_syms, ntemp)
          break
        end
      end
    end
  end
  if !byterepresentable(offset)
    if length(mult_syms) == 1
      mlt, sym = only(mult_syms)
      if !byterepresentable(mlt)
        # this is so we don't unnecessarily add a separate offset
        muladd_index!(
          ls,
          opparents,
          loopdependencies,
          reduceddeps,
          indices,
          offsets,
          strides,
          loopedindex,
          mlt,
          sym,
          offset
        )
        return vptrarray
      end
    end
    r = copysign(abs(offset) & 127, offset)
    vptrarray = gesp_const_offset!(
      ls,
      vptrarray,
      ninds,
      indices,
      loopedindex,
      1,
      offset - r,
      D
    )
    offset = r
  end
  # (success && byterepresentable(offset)) || return false, vptrarray
  if length(mult_syms) == 0
    addconstindex!(indices, offsets, strides, loopedindex, offset)
    return vptrarray
  elseif length(mult_syms) == 1
    mlt, sym = mult_syms[1]
    if sym ∈ ls.loopsymbols
      if byterepresentable(mlt)
        _addoffset!(
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          sym,
          offset,
          mlt
        )
      else
        muladd_index!(
          ls,
          opparents,
          loopdependencies,
          reduceddeps,
          indices,
          offsets,
          strides,
          loopedindex,
          mlt,
          sym,
          offset
        )
      end
    elseif !byterepresentable(mlt)
      muladd_index!(
        ls,
        opparents,
        loopdependencies,
        reduceddeps,
        indices,
        offsets,
        strides,
        loopedindex,
        mlt,
        sym,
        offset
      )
    else
      mop = get(ls.opdict, sym, nothing)
      if mop === nothing
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          mlt,
          sym,
          D
        )
        addconstindex!(indices, offsets, strides, loopedindex, offset)
      elseif isconstant(mop)
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          mlt,
          name(mop),
          D
        )
        addconstindex!(indices, offsets, strides, loopedindex, offset)
      elseif (instruction(mop).instr === :add_fast) ||
             (instruction(mop).instr === :sub_fast)
        vptrarray = add_additive_index!(
          ls,
          opparents,
          vptrarray,
          sym,
          mop,
          ninds,
          indices,
          offsets,
          strides,
          loopedindex,
          loopdependencies,
          reduceddeps,
          offset,
          mlt,
          D
        )
      else
        muladd_index!(
          ls,
          opparents,
          loopdependencies,
          reduceddeps,
          indices,
          offsets,
          strides,
          loopedindex,
          mlt,
          sym,
          offset
        )
      end
    end
    return vptrarray
  end
  loopsym_ind = 0
  operations = Operation[]
  operation_mults = Int[]
  deleteat_inds = Int[]
  for (i, (m, s)) ∈ enumerate(mult_syms)
    if s ∈ ls.loopsymbols
      if loopsym_ind == 0
        loopsym_ind = i
        push!(deleteat_inds, loopsym_ind)
      else
        push!(operations, add_loopvalue!(ls, s, sizeof(Int)))
        push!(deleteat_inds, i)
        push!(operation_mults, m)
      end
    else
      sop = get(ls.opdict, s, nothing)
      if sop === nothing
        push!(deleteat_inds, i)
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          m,
          s,
          D
        )
      elseif isconstant(sop)
        push!(deleteat_inds, i)
        vptrarray = gesp_const_offset!(
          ls,
          vptrarray,
          ninds,
          indices,
          loopedindex,
          m,
          name(sop),
          D
        )
      else
        # @show sop
        # if instruction(sop).instr === :sub_fast
        #   if length(parents(sop)) == 1
        #     sub1 = parents(sop)[1]
        #   elseif length(parents(op)) == 2
        #     sub1 = parents(sop)[1]
        #     sub2 = parents(sop)[2]
        #     if isloopvalue(sub1)
        #     elseif isloopvalue(sub2)
        #       @show sub2
        #     end
        #   end
        # elseif instruction(op).instr === :add_fast
        # end
        push!(operations, getop(ls, s, 8))
        push!(operation_mults, m)
        push!(deleteat_inds, i)
      end
    end
  end
  if (length(operations) > 0) & (loopsym_ind > 0) # turn into an operation
    _m, _s = mult_syms[loopsym_ind]
    push!(operations, add_loopvalue!(ls, _s, sizeof(Int)))
    push!(operation_mults, _m)
  end
  if length(operations) == 0
    if loopsym_ind == 0
      addconstindex!(indices, offsets, strides, loopedindex, offset)
    else
      mlt, sym = mult_syms[loopsym_ind]
      _addoffset!(
        indices,
        offsets,
        strides,
        loopedindex,
        loopdependencies,
        sym,
        offset,
        mlt
      )
    end
    deleteat!(mult_syms, deleteat_inds)
    return gesp_const_offsets!(
      ls,
      vptrarray,
      ninds,
      indices,
      loopedindex,
      mult_syms,
      D
    )
  end
  deleteat!(mult_syms, deleteat_inds)
  vptrarray = gesp_const_offsets!(
    ls,
    vptrarray,
    ninds,
    indices,
    loopedindex,
    mult_syms,
    D
  )
  if length(operations) == 1
    _mlt = only(operation_mults)
    indop = muladd_op!(
      ls,
      Core.ifelse(byterepresentable(_mlt), 1, _mlt),
      only(operations),
      0
    )
    addopindex!(
      opparents,
      loopdependencies,
      reduceddeps,
      indices,
      offsets,
      strides,
      loopedindex,
      indop,
      Core.ifelse(byterepresentable(_mlt), _mlt % Int8, one(Int8)),
      offset % Int8
    )
  else
    mlt1ind = findfirst(isone, operation_mults)
    opbase = if mlt1ind === nothing # if none of them have a multiplier of `1`, pick the base arbtirarility
      muladd_op!(ls, pop!(operation_mults), pop!(operations), 0)
    else # otherwise, we start accumulating with a 1-multiplier.
      deleteat!(operation_mults, mlt1ind)
      popat!(operations, mlt1ind)
    end
    for i ∈ eachindex(operations)
      _op = operations[i]
      _mlt = operation_mults[i]
      opbase = if _mlt == -1
        add_compute!(
          ls,
          gensym!(ls, "indexaccum"),
          instruction(:(-)),
          [opbase, _op],
          sizeof(Int)
        )
      elseif _mlt == 1
        add_compute!(
          ls,
          gensym!(ls, "indexaccum"),
          instruction(:(+)),
          [opbase, _op],
          sizeof(Int)
        )
      else
        add_compute!(
          ls,
          gensym!(ls, "indexaccum"),
          instruction(:muladd),
          [add_constant!(ls, _mlt, sizeof(Int)), _op, opbase],
          sizeof(Int)
        )
      end
    end
    addopindex!(
      opparents,
      loopdependencies,
      reduceddeps,
      indices,
      offsets,
      strides,
      loopedindex,
      opbase,
      one(Int8),
      offset % Int8
    )
  end
  return vptrarray
end

function move_to_last!(x, i)
  i == length(x) && return
  xᵢ = x[i]
  deleteat!(x, i)
  push!(x, xᵢ)
  nothing
end
# TODO: Make this work with Cartesian Indices
function repeated_index!(
  ls::LoopSet,
  indices::Vector{Symbol},
  vptr::Symbol,
  indnum::Int,
  firstind::Int
)
  # Move ind to last position
  vptrrepremoved =
    Symbol(vptr, "##ind##", firstind, "##repeated##", indnum, "##")
  f = Expr(
    :(.),
    Expr(:(.), :LoopVectorization, QuoteNode(:VectorizationBase)),
    QuoteNode(:double_index)
  )
  fiv = Expr(:call, Expr(:curly, :Val, firstind - 1))
  siv = Expr(:call, Expr(:curly, :Val, indnum - 1))
  pushpreamble!(ls, Expr(:(=), vptrrepremoved, Expr(:call, f, vptr, fiv, siv)))
  vptrrepremoved
end

function array_reference_meta!(
  ls::LoopSet,
  array::Symbol,
  rawindices,
  elementbytes::Int,
  var::Union{Nothing,Symbol} = nothing
)
  vptrarray = vptr(array)
  add_vptr!(ls, array, vptrarray) # now, subset
  indices = Symbol[]
  offsets = Int8[]
  strides = Int8[]
  loopedindex = Bool[]
  parents = Operation[]
  loopdependencies = Symbol[]
  reduceddeps = Symbol[]
  loopset = ls.loopsymbols
  ninds = 1
  D = length(rawindices)
  for ind ∈ rawindices
    if ind isa Integer # subset
      if byterepresentable(ind)
        addconstindex!(indices, offsets, strides, loopedindex, ind)
        ninds += 1
      else
        # convert ind to reduce invalidations
        vptrarray = subset_vptr!(
          ls,
          vptrarray,
          ninds,
          convert(Int, ind),
          indices,
          loopedindex,
          0
        )
        length(indices) == 0 && push!(indices, DISCONTIGUOUS)
      end
    elseif ind isa Expr
      #FIXME: position (in loopnest) wont be length(ls.loopsymbols) in general
      vptrarray = checkforoffset!(
        ls,
        vptrarray,
        ninds,
        parents,
        indices,
        offsets,
        strides,
        loopedindex,
        loopdependencies,
        reduceddeps,
        ind,
        D
      )
      ninds += 1
    elseif ind isa Symbol
      if ind ∈ loopset
        ind_prev_index = findfirst(Base.Fix2(===, ind), indices)
        if ind_prev_index === nothing
          push!(indices, ind)
          ninds += 1
          push!(offsets, zero(Int8))
          push!(strides, one(Int8))
          push!(loopedindex, true)
          push!(loopdependencies, ind)
        else
          move_to_last!(indices, ind_prev_index)
          move_to_last!(offsets, ind_prev_index)
          move_to_last!(strides, ind_prev_index)
          move_to_last!(loopedindex, ind_prev_index)
          if length(loopdependencies) == length(indices)
            move_to_last!(loopdependencies, ind_prev_index)
          else
            move_to_last!(
              loopdependencies,
              findfirst(Base.Fix2(===, ind), loopdependencies)::Int
            )
          end
          vptrarray = repeated_index!(
            ls,
            indices,
            vptrarray,
            ninds,
            ind_prev_index - (first(indices) === DISCONTIGUOUS)
          )
          makediscontiguous!(indices)
        end
      else
        indop = get(ls.opdict, ind, nothing)
        if indop !== nothing && !isconstant(indop)
          if iscompute(indop) &&
             Base.sym_in(instruction(indop).instr, (:add_fast, :sub_fast))
            vptrarray = add_additive_index!(
              ls,
              parents,
              vptrarray,
              ind,
              indop,
              ninds,
              indices,
              offsets,
              strides,
              loopedindex,
              loopdependencies,
              reduceddeps,
              0,
              1,
              D
            )
            ninds += 1
          else
            pushparent!(parents, loopdependencies, reduceddeps, indop)
            push!(indices, name(indop))
            ninds += 1
            push!(offsets, zero(Int8))
            push!(strides, one(Int8))
            push!(loopedindex, false)
          end
        else
          vptrarray =
            subset_vptr!(ls, vptrarray, ninds, ind, indices, loopedindex, 0)
          length(indices) == 0 && push!(indices, DISCONTIGUOUS)
        end
      end
    else
      throw("Unrecognized loop index: $ind.")
    end
  end
  mref = ArrayReferenceMeta(
    ArrayReference(array, indices, offsets, strides),
    loopedindex,
    vptrarray
  )
  ArrayReferenceMetaPosition(
    mref,
    parents,
    loopdependencies,
    reduceddeps,
    var === nothing ? Symbol("") : var
  )
end
function tryrefconvert(
  ls::LoopSet,
  ex::Expr,
  elementbytes::Int,
  var::Union{Nothing,Symbol} = nothing
)::Tuple{Bool,ArrayReferenceMetaPosition}
  ya, yinds = if ex.head === :ref
    ref_from_ref!(ls, ex)
  elseif ex.head === :call
    f = first(ex.args)
    if f === :getindex
      ref_from_getindex!(ls, ex)
    elseif f === :setindex!
      ref_from_setindex!(ls, ex)
    else
      return false, NOTAREFERENCEMP
    end
  else
    return false, NOTAREFERENCEMP
  end
  true, array_reference_meta!(ls, ya, yinds, elementbytes, var)
end
