FluxML / IRTools.jl

Mike's Little Intermediate Representation
MIT License
111 stars 36 forks source link

Bounds error in dynamo with dummy metadata #44

Open phipsgabler opened 4 years ago

phipsgabler commented 4 years ago

I know I shouldn't build IR from scratch, but I really wonder why the following happens:

julia> plus_dummy(x, y) = nothing
plus_dummy (generic function with 1 method)

julia> @dynamo function foo(X, Y)
           ir = IR(typeof(plus_dummy), X, Y)
           @show ir
           @show _args = IRTools.arguments(ir)
           s = push!(ir, xcall(:+, _args[end-1], _args[end]))
           @show ir
           return!(ir, s)
           @show ir
           return ir
       end

julia> foo(1, 2)
ir = 1: (%1, %2, %3)
  return :(Main.nothing)
_args = IRTools.arguments(ir) = Any[%1, %2, %3]
ir = 1: (%1, %2, %3)
  %4 = %2 + %3
  return :(Main.nothing)
ir = 1: (%1, %2, %3)
  %4 = %2 + %3
  return %4
ERROR: BoundsError: attempt to access (1, 2)
  at index [3]
Stacktrace:
 [1] foo(::Int64, ::Int64) at /home/philipp/.julia/packages/IRTools/Fl3dY/src/reflection/dynamo.jl:44
 [2] top-level scope at REPL[7]:1
MikeInnes commented 4 years ago

The lowered code gives a good idea:

julia> @code_lowered foo(1, 2)
CodeInfo(
1 ─      Base.getfield(args, 1)
│   %2 = Base.getfield(args, 2)
│   %3 = Base.getfield(args, 3)
│   %4 = %2 + %3
└──      return %4
)

The IR for plus_dummy is expecting three arguments (itself, x and y) but the dynamo actually only takes two (X and Y). If you delete the self argument it works fine:

@dynamo function foo(X, Y)
  ir = IR(typeof(plus_dummy), X, Y)
  _args = IRTools.arguments(ir)
  deletearg!(block(ir, 1), 1)
  s = push!(ir, xcall(:+, _args[end-1], _args[end]))
  return!(ir, s)
  return ir
end

I know I shouldn't build IR from scratch

FWIW, I don't see any reason why we shouldn't support this well. I think I'd ideally write this as

@dynamo function foo(X, Y)
  ir = IR()
  args = argument!(ir)
  x = push!(ir, xcall(:getindex, args, 1))
  y = push!(ir, xcall(:getindex, args, 2))
  return!(ir, xcall(:+, x, y))
end

The only thing we need is (1) a standardised calling convention for IR without metadata (here I'm assuming that all args are passed in a single args tuple) and (2) some machinery to convert such IR to a codeinfo (which would look a lot like what you've done here, just inside the @dynamo).