diff --git a/base/expr.jl b/base/expr.jl
index 875bf5b56f965..e4d8a255fec99 100644
--- a/base/expr.jl
+++ b/base/expr.jl
@@ -133,7 +133,7 @@ end
 
 function popmeta!(body::Expr, sym::Symbol)
     body.head == :block || return false, []
-    found, metaex = findmeta_block(body)
+    found, metaex = findmeta_block(body.args)
     if !found
         return false, []
     end
@@ -151,23 +151,29 @@ function popmeta!(body::Expr, sym::Symbol)
     false, []
 end
 popmeta!(arg, sym) = (false, [])
+function popmeta!(body::Array{Any,1}, sym::Symbol)
+    ex = Expr(:block); ex.args = body
+    popmeta!(ex, sym)
+end
 
 function findmeta(ex::Expr)
     if ex.head == :function || (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call)
         body::Expr = ex.args[2]
         body.head == :block || error(body, " is not a block expression")
-        return findmeta_block(ex)
+        return findmeta_block(ex.args)
     end
     error(ex, " is not a function expression")
 end
 
-function findmeta_block(ex::Expr)
-    for a in ex.args
+findmeta(ex::Array{Any,1}) = findmeta_block(ex)
+
+function findmeta_block(exargs)
+    for a in exargs
         if isa(a, Expr)
             if (a::Expr).head == :meta
                 return true, a::Expr
             elseif (a::Expr).head == :block
-                found, exb = findmeta_block(a)
+                found, exb = findmeta_block(a.args)
                 if found
                     return found, exb
                 end
diff --git a/base/inference.jl b/base/inference.jl
index a7421d11bb7eb..e7d524bb694ac 100644
--- a/base/inference.jl
+++ b/base/inference.jl
@@ -20,11 +20,8 @@ end
 
 type InferenceState
     atypes #::Type       # type sig
-    ast #::Expr
-    body::Array{Any,1}   # ast body
     sp::SimpleVector     # static parameters
     gensym_types::Array{Any,1} # types of the GenSym's in this function
-    vinfo::Array{Any,1}  # variable properties
     label_counter::Int   # index of the current highest label for this function
     fedbackvars::Dict{GenSym, Bool}
     mod::Module
@@ -33,6 +30,7 @@ type InferenceState
 
     # info on the state of inference and the linfo
     linfo::LambdaInfo
+    destination::LambdaInfo   # results need to be copied here when we finish
     nargs::Int
     stmt_types::Vector{Any}
     # return type
@@ -60,15 +58,10 @@ type InferenceState
     inferred::Bool
     tfunc_idx::Int
 
-    function InferenceState(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, ast, optimize::Bool)
-        if !isa(ast,Expr)
-            ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast)
-        end
-        assert(is(ast.head,:lambda))
-        vinflist = ast.args[2][1]::Array{Any,1}
-        nslots = length(vinflist)
-        body = (ast.args[3].args)::Array{Any,1}
-        nl = label_counter(body)+1
+    function InferenceState(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, optimize::Bool)
+        @assert isa(linfo.code,Array{Any,1})
+        nslots = length(linfo.slotnames)
+        nl = label_counter(linfo.code)+1
 
         if length(linfo.sparam_vals) > 0
             sp = linfo.sparam_vals
@@ -78,15 +71,14 @@ type InferenceState
             sp = sparams
         end
 
-        n = length(body)
+        n = length(linfo.code)
         s = Any[ () for i=1:n ]
         # initial types
         s[1] = Any[ VarState(Bottom,true) for i=1:nslots ]
 
-        la = length(ast.args[1])
+        la = linfo.nargs
         if la > 0
-            lastarg = ast.args[1][la]
-            if is_rest_arg(lastarg)
+            if linfo.isva
                 if atypes === Tuple
                     if la > 1
                         atypes = Tuple{Any[Any for i=1:la-1]..., Tuple.parameters[1]}
@@ -126,8 +118,8 @@ type InferenceState
             @assert la == 0 # wrong number of arguments
         end
 
-        gensym_uses = find_gensym_uses(body)
-        gensym_types = Any[ NF for i=1:length(gensym_uses) ]
+        gensym_uses = find_gensym_uses(linfo.code)
+        gensym_types = linfo.gensymtypes
         gensym_init = copy(gensym_types)
 
         # exception handlers
@@ -139,9 +131,9 @@ type InferenceState
         push!(W, 1) #initial pc to visit
 
         frame = new(
-            atypes, ast, body, sp, gensym_types, vinflist, nl, Dict{GenSym, Bool}(), linfo.module, 0, false,
+            atypes, sp, gensym_types, nl, Dict{GenSym, Bool}(), linfo.module, 0, false,
 
-            linfo, la, s, Union{}, W, n,
+            linfo, linfo, la, s, Union{}, W, n,
             cur_hand, handler_at, n_handlers,
             gensym_uses, gensym_init,
             ObjectIdDict(), #Dict{InferenceState, Vector{LineNum}}(),
@@ -1504,8 +1496,34 @@ function newvar!(sv::InferenceState, typ)
     return GenSym(id)
 end
 
-is_rest_arg(arg::ANY) = (ccall(:jl_is_rest_arg,Int32,(Any,), arg) != 0)
-
+# copy a LambdaInfo just enough to make it not share data with li.def
+function unshare_linfo(li::LambdaInfo)
+    if li.nargs > 0
+        if li === li.def
+            li = ccall(:jl_copy_lambda_info, Any, (Any,), li)::LambdaInfo
+        end
+        if !isa(li.code, Array{Any,1})
+            li.code = ccall(:jl_uncompress_ast, Any, (Any,Any), li, li.code)
+        elseif li.code === li.def.code && li !== li.def
+            li.code = astcopy(li.code)
+        end
+        li.slotnames = copy(li.slotnames)
+        li.slotflags = copy(li.slotflags)
+        if isa(li.slottypes,Array)
+            li.slottypes = copy(li.slottypes)
+        end
+        if isa(li.gensymtypes,Array)
+            li.gensymtypes = copy(li.gensymtypes)
+        end
+    end
+    if !isa(li.slottypes,Array)
+        li.slottypes = Any[ Any for i = 1:length(li.slotnames) ]
+    end
+    if !isa(li.gensymtypes,Array)
+        li.gensymtypes = Any[ NF for i = 1:(li.gensymtypes::Int) ]
+    end
+    return li
+end
 
 #### entry points for inferring a LambdaInfo given a type signature ####
 
@@ -1514,8 +1532,8 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee
     if linfo.module === Core && isempty(sparams) && isempty(linfo.sparam_vals)
         atypes = Tuple
     end
-    local ast::Expr, tfunc_idx = -1
-    curtype = Bottom
+    local tfunc_idx = -1
+    local frame
     # check cached t-functions
     # linfo.def is the original unspecialized version of a method.
     # we aggregate all saved type inference data there.
@@ -1528,33 +1546,22 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee
                 if isa(code, InferenceState)
                     # inference on this signature is in progress
                     frame = code
-                    tfunc_idx = i
                     if linfo.inInference
                         # record the LambdaInfo where this result should be cached when it is finished
-                        @assert !frame.linfo.inInference
-                        frame.linfo = linfo
+                        @assert frame.destination === frame.linfo || frame.destination === linfo
+                        frame.destination = linfo
                     end
+                    tfunc_idx = i
+                    break
                 elseif isa(code, Type)
-                    curtype = code::Type
                     # sometimes just a return type is stored here. if a full AST
                     # is not needed, we can return it.
                     if !needtree
                         return (nothing, code, true)
                     end
-                elseif isa(code,Tuple)
-                    inftree, inftyp = code
-                    if linfo.inInference
-                        linfo.inferred = true
-                        linfo.ast = inftree
-                        linfo.rettype = inftyp
-                        linfo.inInference = false
-                        if !isa(inftree,Expr)
-                            inftree = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, inftree)
-                        end
-                        linfo.nslots = length(inftree.args[2][1])
-                        linfo.ngensym = length(inftree.args[2][3])
-                    end
-                    return (inftree, inftyp, true) # code is a tuple (ast, type)
+                elseif isa(code,LambdaInfo)
+                    @assert code.inferred
+                    return (code, code.rettype, true)
                 else
                     # otherwise this is an InferenceState from a different bootstrap stage's
                     # copy of the inference code; ignore it.
@@ -1591,9 +1598,9 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee
             end
         end
         # add lam to be inferred and record the edge
-        ast = ccall(:jl_prepare_ast, Any, (Any,), linfo)::Expr
+        linfo = unshare_linfo(linfo)
         # our stack frame inference context
-        frame = InferenceState(linfo, atypes, sparams, ast, optimize)
+        frame = InferenceState(linfo, atypes, sparams, optimize)
 
         if cached
             #println(linfo)
@@ -1627,7 +1634,7 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee
         end
     end
     typeinf_loop(frame)
-    return (frame.ast, frame.bestguess, frame.inferred)
+    return (frame.linfo, frame.bestguess, frame.inferred)
 end
 
 function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, caller)
@@ -1644,7 +1651,21 @@ function typeinf_uncached(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector,
     return typeinf_edge(linfo, atypes, sparams, true, optimize, false, nothing)
 end
 function typeinf_ext(linfo::LambdaInfo, toplevel::Bool)
-    return typeinf_edge(linfo, linfo.specTypes, svec(), true, true, true, nothing)
+    (code, _t, _) = typeinf_edge(linfo, linfo.specTypes, svec(), true, true, true, nothing)
+    if code.inferred
+        linfo.inferred = true
+        linfo.inInference = false
+        if linfo !== code
+            linfo.code = code.code
+            linfo.slotnames = code.slotnames
+            linfo.slottypes = code.slottypes
+            linfo.slotflags = code.slotflags
+            linfo.gensymtypes = code.gensymtypes
+            linfo.rettype = code.rettype
+            linfo.pure = code.pure
+        end
+    end
+    nothing
 end
 
 
@@ -1728,7 +1749,7 @@ function typeinf_frame(frame)
             else
                 frame.cur_hand = frame.handler_at[pc]
             end
-            stmt = frame.body[pc]
+            stmt = frame.linfo.code[pc]
             changes = abstract_interpret(stmt, s[pc]::Array{Any,1}, frame)
             if changes === ()
                 # if there was a Expr(:static_typeof) on this line,
@@ -1955,41 +1976,46 @@ function finish(me::InferenceState)
             me.gensym_types[i] = Union{}
         end
     end
-    fulltree = type_annotate(me.ast, me.stmt_types, me, me.bestguess, me.nargs)
+    type_annotate!(me.linfo, me.stmt_types, me, me.bestguess, me.nargs)
 
     # make sure (meta pure) is stripped from full tree
-    @assert fulltree.args[3].head === :body
-    body = Expr(:block)
-    body.args = fulltree.args[3].args::Array{Any,1}
-    ispure = popmeta!(body, :pure)[1]
+    ispure = popmeta!(me.linfo.code, :pure)[1]
 
     # run optimization passes on fulltree
     if me.optimize
         if JLOptions().can_inline == 1
-            fulltree.args[3] = inlining_pass(fulltree.args[3], me, fulltree)[1]
-            inbounds_meta_elim_pass(fulltree.args[3])
+            inlining_pass!(me.linfo, me)
+            inbounds_meta_elim_pass!(me.linfo.code)
         end
-        alloc_elim_pass(fulltree, me)
-        getfield_elim_pass(fulltree.args[3], me)
-        reindex_labels!(fulltree.args[3], me)
+        alloc_elim_pass!(me.linfo, me)
+        getfield_elim_pass!(me.linfo, me)
+        reindex_labels!(me.linfo, me)
     end
 
     # finalize and record the linfo result
-    me.ast = fulltree
     me.inferred = true
 
-    compressedtree = ccall(:jl_compress_ast, Any, (Any,Any), me.linfo.def, fulltree)
-    if me.linfo.inInference
-        me.linfo.inferred = true
-        me.linfo.pure = ispure
-        me.linfo.ast = compressedtree
-        me.linfo.rettype = me.bestguess
-        me.linfo.inInference = false
-        me.linfo.nslots = length(fulltree.args[2][1])
-        me.linfo.ngensym = length(me.gensym_types)
+    me.linfo.inferred = true
+    me.linfo.inInference = false
+    me.linfo.pure = ispure
+    compressedtree = ccall(:jl_compress_ast, Any, (Any,Any), me.linfo.def, me.linfo.code)
+    me.linfo.code = compressedtree
+    me.linfo.rettype = me.bestguess
+
+    if me.destination !== me.linfo
+        out = me.destination
+        out.inferred = true
+        out.inInference = false
+        out.code = me.linfo.code
+        out.slotnames = me.linfo.slotnames
+        out.slottypes = me.linfo.slottypes
+        out.slotflags = me.linfo.slotflags
+        out.gensymtypes = me.linfo.gensymtypes
+        out.rettype = me.linfo.rettype
+        out.pure = me.linfo.pure
     end
     if me.tfunc_idx != -1
-        me.linfo.def.tfunc[me.tfunc_idx + 1] = (compressedtree, me.bestguess)
+        me.linfo.def.tfunc[me.tfunc_idx + 1] = me.linfo
         me.linfo.def.tfunc[me.tfunc_idx + 2] = false
     end
 
@@ -2064,7 +2090,7 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs)
 end
 
 # annotate types of all symbols in AST
-function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, nargs)
+function type_annotate!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, rettype::ANY, nargs)
     nslots = length(states[1])
     decls = Any[ NF for i = 1:nslots ]
     undefs = fill(false, nslots)
@@ -2072,7 +2098,7 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, n
     for i = 1:nargs
         decls[i] = states[1][i].typ
     end
-    body = ast.args[3].args::Array{Any,1}
+    body = linfo.code
     for i=1:length(body)
         st_i = states[i]
         if st_i !== ()
@@ -2080,21 +2106,17 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, n
             body[i] = eval_annotate(body[i], st_i, sv, decls, undefs)
         end
     end
-    ast.args[3].typ = rettype
 
     # add declarations for variables that are always the same type
     for i = 1:nslots
-        vi = ast.args[2][1][i]
         if decls[i] !== NF
-            vi[2] = decls[i]
+            linfo.slottypes[i] = decls[i]
         end
         if undefs[i]
-            vi[3] |= 32
+            linfo.slotflags[i] |= 32
         end
     end
-    ast.args[2][3] = sv.gensym_types
-
-    return ast
+    nothing
 end
 
 # replace slots 1:na with argexprs, static params with spvals, and increment
@@ -2281,7 +2303,7 @@ end
 # static parameters are ok if all the static parameter values are leaf types,
 # meaning they are fully known.
 # `ft` is the type of the function. `f` is the exact function if known, or else `nothing`.
-function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::InferenceState, enclosing_ast::Expr)
+function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::InferenceState, enclosing::LambdaInfo)
     local linfo,
         metharg::Type,
         argexprs = e.args,
@@ -2403,7 +2425,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
     #     # check call stack to see if this argument list is growing
     #     st = inference_stack
     #     while !isa(st, EmptyCallStack)
-    #         if st.ast === linfo.def.ast && length(atypes) > length(st.types)
+    #         if st.code === linfo.def.code && length(atypes) > length(st.types)
     #             atypes = limit_tuple_type(atypes)
     #             meth = _methods(f, atypes, 1)
     #             if meth === false || length(meth) != 1
@@ -2424,21 +2446,21 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
     methargs = metharg.parameters
     nm = length(methargs)
 
-    (ast, ty, inferred) = typeinf(linfo, metharg, methsp, true)
-    if is(ast,nothing) || !inferred
+    (linfo, ty, inferred) = typeinf(linfo, metharg, methsp, true)
+    if is(linfo,nothing) || !inferred
         return NF
     end
+    ast = linfo.code
 
-    if !isa(ast,Expr)
+    if !isa(ast,Array{Any,1})
         ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast)
     else
         ast = astcopy(ast)
     end
-    ast = ast::Expr
-    vinflist = ast.args[2][1]::Array{Any,1}
+    ast = ast::Array{Any,1}
 
     body = Expr(:block)
-    body.args = ast.args[3].args::Array{Any,1}
+    body.args = ast
     propagate_inbounds, _ = popmeta!(body, :propagate_inbounds)
     cost::Int = 1000
     if incompletematch
@@ -2483,12 +2505,11 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
     filter!(x->!(isa(x,Expr) && x.head === :meta && isempty(x.args)),
             body.args)
 
-    enc_vinflist = enclosing_ast.args[2][1]::Array{Any,1}
-    na = length(ast.args[1])
+    na = linfo.nargs
 
     # check for vararg function
     isva = false
-    if na > 0 && is_rest_arg(ast.args[1][na])
+    if na > 0 && linfo.isva
         if length(argexprs) < na - 1
             return (Expr(:call, TopNode(:error), "too few arguments"), [])
         end
@@ -2592,9 +2613,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
 
         islocal = false # if the argument name is also used as a local variable,
                         # we need to keep it as a variable name
-        if vinflist[i][3] != 0
+        if linfo.slotflags[i] != 0
             islocal = true
-            aeitype = tmerge(aeitype, vinflist[i][2])
+            aeitype = tmerge(aeitype, linfo.slottypes[i])
         end
 
         # ok for argument to occur more than once if the actual argument
@@ -2647,7 +2668,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
                     vnew = newvar!(sv, aeitype)
                     argexprs[i] = vnew
                 else
-                    vnew = add_slot!(enclosing_ast, aeitype, #=SSA=#false)
+                    vnew = add_slot!(enclosing, aeitype, #=SSA=#false)
                     argexprs[i] = vnew
                 end
                 unshift!(stmts, Expr(:(=), vnew, aei))
@@ -2669,23 +2690,20 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
     end
 
     # re-number the GenSyms and copy their type-info to the new ast
-    gensym_types = ast.args[2][3]
-    if gensym_types != 0
-        if (isa(gensym_types,Integer))
-            gensym_types = Any[Any for i = 1:ast.args[2][3]]
-        end
-        if !isempty(gensym_types)
-            incr = length(sv.gensym_types)
-            if incr != 0
-                body = gensym_increment(body, incr)
-            end
-            append!(sv.gensym_types, ast.args[2][3])
+    gensym_types = linfo.gensymtypes
+    if !isempty(gensym_types)
+        incr = length(sv.gensym_types)
+        if incr != 0
+            body = gensym_increment(body, incr)
         end
+        append!(sv.gensym_types, gensym_types)
     end
 
     # ok, substitute argument expressions for argument names in the body
-    body = substitute!(body, na, argexprs, spvals, length(enc_vinflist)-na)
-    append!(enc_vinflist, vinflist[na+1:end])
+    body = substitute!(body, na, argexprs, spvals, length(enclosing.slotnames)-na)
+    append!(enclosing.slotnames, linfo.slotnames[na+1:end])
+    append!(enclosing.slottypes, linfo.slottypes[na+1:end])
+    append!(enclosing.slotflags, linfo.slotflags[na+1:end])
 
     # make labels / goto statements unique
     newlabels = zeros(Int,label_counter(body.args)+1)
@@ -2715,7 +2733,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
 
     # convert return statements into a series of goto's
     retstmt = genlabel(sv)
-    rettype = (ast.args[3]::Expr).typ
+    rettype = linfo.rettype
     local retval
     multiret = false
     lastexpr = pop!(body.args)
@@ -2734,7 +2752,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
             if a.head === :return
                 if !multiret
                     # create slot first time
-                    retval = add_slot!(enclosing_ast, rettype, false)
+                    retval = add_slot!(enclosing, rettype, false)
                 end
                 multiret = true
                 unshift!(a.args, retval)
@@ -2838,7 +2856,31 @@ end
 
 const corenumtype = Union{Int32,Int64,Float32,Float64}
 
-function inlining_pass(e::Expr, sv, ast)
+function inlining_pass!(linfo::LambdaInfo, sv::InferenceState)
+    eargs = linfo.code
+    stmts = []
+    i = 1
+    while i <= length(eargs)
+        ei = eargs[i]
+        if isa(ei,Expr)
+            res = inlining_pass(ei, sv, linfo)
+            eargs[i] = res[1]
+        else
+            i += 1
+            continue
+        end
+        if isa(res[2],Array)
+            sts = res[2]::Array{Any,1}
+            for j = 1:length(sts)
+                insert!(eargs, i, sts[j])
+                i += 1
+            end
+        end
+        i += 1
+    end
+end
+
+function inlining_pass(e::Expr, sv, linfo)
     if e.head === :method
         # avoid running the inlining pass on function definitions
         return (e,())
@@ -2848,25 +2890,6 @@ function inlining_pass(e::Expr, sv, ast)
         return (e,())
     end
     stmts = []
-    if e.head === :body
-        i = 1
-        while i <= length(eargs)
-            ei = eargs[i]
-            if isa(ei,Expr)
-                res = inlining_pass(ei, sv, ast)
-                eargs[i] = res[1]
-                if isa(res[2],Array)
-                    sts = res[2]::Array{Any,1}
-                    for j = 1:length(sts)
-                        insert!(eargs, i, sts[j])
-                        i += 1
-                    end
-                end
-            end
-            i += 1
-        end
-        return (e, stmts)
-    end
     arg1 = eargs[1]
     # don't inline first (global) arguments of ccall, as this needs to be evaluated
     # by the interpreter and inlining might put in something it can't handle,
@@ -2895,7 +2918,7 @@ function inlining_pass(e::Expr, sv, ast)
             else
                 argloc = eargs
             end
-            res = inlining_pass(ei::Expr, sv, ast)
+            res = inlining_pass(ei::Expr, sv, linfo)
             res1 = res[1]
             if has_stmts && !effect_free(res1, sv, false)
                 restype = exprtype(res1,sv)
@@ -2970,7 +2993,7 @@ function inlining_pass(e::Expr, sv, ast)
             (a === Bottom || isvarargtype(a)) && return (e, stmts)
             ata[i] = a
         end
-        res = inlineable(f, ft, e, ata, sv, ast)
+        res = inlineable(f, ft, e, ata, sv, linfo)
         if isa(res,Tuple)
             if isa(res[2],Array) && !isempty(res[2])
                 append!(stmts,res[2])
@@ -3029,10 +3052,11 @@ function inlining_pass(e::Expr, sv, ast)
     return (e,stmts)
 end
 
-function add_slot!(ast, typ, is_sa)
-    vinflist = ast.args[2][1]::Array{Any,1}
-    id = length(vinflist)+1
-    push!(vinflist, Any[:__temp__, typ, 2+16*is_sa])
+function add_slot!(linfo::LambdaInfo, typ, is_sa)
+    id = length(linfo.slotnames)+1
+    push!(linfo.slotnames, :__temp__)
+    push!(linfo.slottypes, typ)
+    push!(linfo.slotflags, 2+16*is_sa)
     Slot(id, typ)
 end
 
@@ -3052,68 +3076,73 @@ function is_known_call_p(e::Expr, pred, sv)
     return !is(f,false) && pred(_ieval(f,sv))
 end
 
-is_var_assigned(ast, v) = isa(v,Slot) && ast.args[2][1][v.id][3]&2 != 0
+is_var_assigned(linfo, v) = isa(v,Slot) && linfo.slotflags[v.id]&2 != 0
 
-function delete_var!(ast, id, T)
+function delete_var!(linfo, id, T)
     filter!(x->!(isa(x,Expr) && (x.head === :(=) || x.head === :const) &&
                  isa(x.args[1],T) && x.args[1].id == id),
-            ast.args[3].args)
-    ast
+            linfo.code)
+    linfo
+end
+
+function slot_replace!(linfo::LambdaInfo, id, rhs, T)
+    for i = 1:length(linfo.code)
+        linfo.code[i] = _slot_replace!(linfo.code[i], id, rhs, T)
+    end
+    linfo
 end
 
-function slot_replace!(e, id, rhs, T)
+function _slot_replace!(e, id, rhs, T)
     if isa(e,T) && e.id == id
         return rhs
     end
     if isa(e,Expr)
         for i = 1:length(e.args)
-            e.args[i] = slot_replace!(e.args[i], id, rhs, T)
+            e.args[i] = _slot_replace!(e.args[i], id, rhs, T)
         end
     end
     return e
 end
 
-occurs_undef(var::Int, expr, varinfo) =
-    varinfo[var][3]&32 != 0 && occurs_more(expr, e->(isa(e,Slot) && e.id==var), 0)>0
+occurs_undef(var::Int, expr, flags) =
+    flags[var]&32 != 0 && occurs_more(expr, e->(isa(e,Slot) && e.id==var), 0)>0
 
 # remove all single-assigned vars v in "v = x" where x is an argument
 # and not assigned.
 # "sa" is the result of find_sa_vars
 # T: Slot or Gensym
-function remove_redundant_temp_vars(ast, sa, T)
-    varinfo = ast.args[2][1]
-    gensym_types = ast.args[2][3]
-    body = ast.args[3]
+function remove_redundant_temp_vars(linfo, sa, T)
+    flags = linfo.slotflags
+    gensym_types = linfo.gensymtypes
+    bexpr = Expr(:block); bexpr.args = linfo.code
     for (v,init) in sa
-        if (isa(init, Slot) && !is_var_assigned(ast, init))
+        if (isa(init, Slot) && !is_var_assigned(linfo, init))
             # this transformation is not valid for vars used before def.
             # we need to preserve the point of assignment to know where to
             # throw errors (issue #4645).
-            if T===GenSym || !occurs_undef(v, body, varinfo)
+            if T===GenSym || !occurs_undef(v, bexpr, flags)
                 # the transformation is not ideal if the assignment
                 # is present for the auto-unbox functionality
                 # (from inlining improved type inference information)
                 # and this transformation would worsen the type information
                 # everywhere later in the function
-                if init.typ <: (T===GenSym ? gensym_types[v+1] : varinfo[v][2])
-                    delete_var!(ast, v, T)
-                    slot_replace!(body, v, init, T)
+                if init.typ <: (T===GenSym ? gensym_types[v+1] : linfo.slottypes[v])
+                    delete_var!(linfo, v, T)
+                    slot_replace!(linfo, v, init, T)
                 end
             end
         end
     end
-    ast
+    linfo
 end
 
 # compute set of slots assigned once
-function find_sa_vars(ast)
-    body = ast.args[3].args
+function find_sa_vars(linfo::LambdaInfo)
+    body = linfo.code
     av = ObjectIdDict()
     av2 = ObjectIdDict()
     gss = ObjectIdDict()
-    vinfos = ast.args[2][1]::Array{Any,1}
-    nargs = length(ast.args[1])
-    args = ast.args[1]
+    nargs = linfo.nargs
     for i = 1:length(body)
         e = body[i]
         if isa(e,Expr) && is(e.head,:(=))
@@ -3171,60 +3200,67 @@ end
 
 # removes inbounds metadata if we never encounter an inbounds=true or
 # boundscheck context in the method body
-function inbounds_meta_elim_pass(e::Expr)
+function inbounds_meta_elim_pass!(code::Array{Any,1})
     if findfirst(x -> isa(x, Expr) &&
                       ((x.head === :inbounds && x.args[1] == true) || x.head === :boundscheck),
-                 e.args) == 0
-        filter!(x -> !(isa(x, Expr) && x.head === :inbounds), e.args)
+                 code) == 0
+        filter!(x -> !(isa(x, Expr) && x.head === :inbounds), code)
     end
 end
 
 # does the same job as alloc_elim_pass for allocations inline in getfields
 # TODO can probably be removed when we switch to a linear IR
-function getfield_elim_pass(e::Expr, sv)
+function getfield_elim_pass!(linfo::LambdaInfo, sv)
+    body = linfo.code
+    for i = 1:length(body)
+        body[i] = _getfield_elim_pass!(body[i], sv)
+    end
+end
+
+function _getfield_elim_pass!(e::Expr, sv)
     for i = 1:length(e.args)
-        ei = e.args[i]
-        if isa(ei,Expr)
-            getfield_elim_pass(ei, sv)
-            if is_known_call(ei, getfield, sv) && length(ei.args)==3 &&
-                (isa(ei.args[3],Int) || isa(ei.args[3],QuoteNode))
-                e1 = ei.args[2]
-                j = ei.args[3]
-                if isa(e1,Expr)
-                    alloc = is_immutable_allocation(e1, sv)
-                    if !is(alloc, false)
-                        flen, fnames = alloc
-                        if isa(j,QuoteNode)
-                            j = findfirst(fnames, j.value)
-                        end
-                        if 1 <= j <= flen
-                            ok = true
-                            for k = 2:length(e1.args)
-                                k == j+1 && continue
-                                if !effect_free(e1.args[k], sv, true)
-                                    ok = false; break
-                                end
-                            end
-                            if ok
-                                e.args[i] = e1.args[j+1]
-                            end
+        e.args[i] = _getfield_elim_pass!(e.args[i], sv)
+    end
+    if is_known_call(e, getfield, sv) && length(e.args)==3 &&
+        (isa(e.args[3],Int) || isa(e.args[3],QuoteNode))
+        e1 = e.args[2]
+        j = e.args[3]
+        if isa(e1,Expr)
+            alloc = is_immutable_allocation(e1, sv)
+            if !is(alloc, false)
+                flen, fnames = alloc
+                if isa(j,QuoteNode)
+                    j = findfirst(fnames, j.value)
+                end
+                if 1 <= j <= flen
+                    ok = true
+                    for k = 2:length(e1.args)
+                        k == j+1 && continue
+                        if !effect_free(e1.args[k], sv, true)
+                            ok = false; break
                         end
                     end
-                elseif isa(e1,Tuple) && isa(j,Int) && (1 <= j <= length(e1))
-                    e1j = e1[j]
-                    if !(isa(e1j,Number) || isa(e1j,AbstractString) || isa(e1j,Tuple) ||
-                         isa(e1j,Type))
-                        e1j = QuoteNode(e1j)
+                    if ok
+                        return e1.args[j+1]
                     end
-                    e.args[i] = e1j
-                elseif isa(e1,QuoteNode) && isa(e1.value,Tuple) && isa(j,Int) && (1 <= j <= length(e1.value))
-                    e.args[i] = QuoteNode(e1.value[j])
                 end
             end
+        elseif isa(e1,Tuple) && isa(j,Int) && (1 <= j <= length(e1))
+            e1j = e1[j]
+            if !(isa(e1j,Number) || isa(e1j,AbstractString) || isa(e1j,Tuple) ||
+                 isa(e1j,Type))
+                e1j = QuoteNode(e1j)
+            end
+            return e1j
+        elseif isa(e1,QuoteNode) && isa(e1.value,Tuple) && isa(j,Int) && (1 <= j <= length(e1.value))
+            return QuoteNode(e1.value[j])
         end
     end
+    return e
 end
 
+_getfield_elim_pass!(e::ANY, sv) = e
+
 # check if e is a successful allocation of an immutable struct
 # if it is, returns (n,f) such that it is always valid to call
 # getfield(..., 1 <= x <= n) or getfield(..., x in f) on the result
@@ -3250,14 +3286,15 @@ function is_immutable_allocation(e :: ANY, sv::InferenceState)
     end
     false
 end
+
 # eliminate allocation of unnecessary immutables
 # that are only used as arguments to safe getfield calls
-function alloc_elim_pass(ast::Expr, sv::InferenceState)
-    bexpr = ast.args[3]::Expr
-    body = (ast.args[3].args)::Array{Any,1}
-    vs, gs = find_sa_vars(ast)
-    remove_redundant_temp_vars(ast, vs, Slot)
-    remove_redundant_temp_vars(ast, gs, GenSym)
+function alloc_elim_pass!(linfo::LambdaInfo, sv::InferenceState)
+    body = linfo.code
+    bexpr = Expr(:block); bexpr.args = body
+    vs, gs = find_sa_vars(linfo)
+    remove_redundant_temp_vars(linfo, vs, Slot)
+    remove_redundant_temp_vars(linfo, gs, GenSym)
     i = 1
     while i < length(body)
         e = body[i]
@@ -3295,14 +3332,14 @@ function alloc_elim_pass(ast::Expr, sv::InferenceState)
                 end
             end
             i += n_ins
-            replace_getfield!(ast, bexpr, var, vals, field_names, sv, i)
+            replace_getfield!(linfo, bexpr, var, vals, field_names, sv, i)
         else
             i += 1
         end
     end
 end
 
-function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0)
+function replace_getfield!(linfo::LambdaInfo, e::Expr, tupname, vals, field_names, sv, i0)
     for i = i0:length(e.args)
         a = e.args[i]
         if isa(a,Expr) && is_known_call(a, getfield, sv) &&
@@ -3321,7 +3358,7 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0)
                 val = val::Slot
                 if a.typ <: val.typ && !typeseq(a.typ,val.typ)
                     val.typ = a.typ
-                    ast.args[2][1][val.id][2] = a.typ
+                    linfo.slottypes[val.id] = a.typ
                 end
             elseif isa(val,GenSym)
                 val = val::GenSym
@@ -3332,25 +3369,26 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0)
             end
             e.args[i] = val
         elseif isa(a, Expr)
-            replace_getfield!(ast, a::Expr, tupname, vals, field_names, sv, 1)
+            replace_getfield!(linfo, a::Expr, tupname, vals, field_names, sv, 1)
         end
     end
 end
 
 # fix label numbers to always equal the statement index of the label
-function reindex_labels!(e, sv)
+function reindex_labels!(linfo::LambdaInfo, sv::InferenceState)
+    body = linfo.code
     mapping = zeros(Int, sv.label_counter)
-    for i = 1:length(e.args)
-        el = e.args[i]
+    for i = 1:length(body)
+        el = body[i]
         if isa(el,LabelNode)
             mapping[el.label] = i
-            e.args[i] = LabelNode(i)
+            body[i] = LabelNode(i)
         end
     end
-    for i = 1:length(e.args)
-        el = e.args[i]
+    for i = 1:length(body)
+        el = body[i]
         if isa(el,GotoNode)
-            e.args[i] = GotoNode(mapping[el.label])
+            body[i] = GotoNode(mapping[el.label])
         elseif isa(el,Expr)
             if el.head === :gotoifnot
                 el.args[2] = mapping[el.args[2]]
diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl
index d23997108a5d0..89aee2e807507 100644
--- a/base/interactiveutil.jl
+++ b/base/interactiveutil.jl
@@ -225,17 +225,19 @@ versioninfo(verbose::Bool) = versioninfo(STDOUT,verbose)
 
 function code_warntype(io::IO, f, t::ANY)
     emph_io = IOContext(io, :TYPEEMPHASIZE => true)
-    ct = code_typed(f, t)
-    for ast in ct
+    for li in code_typed(f, t)
         println(emph_io, "Variables:")
-        vars = ast.args[2][1]
-        for v in vars
-            print(emph_io, "  ", v[1])
-            show_expr_type(emph_io, v[2], true)
+        for i = 1:length(li.slotnames)
+            print(emph_io, "  ", li.slotnames[i])
+            if isa(li.slottypes,Array)
+                show_expr_type(emph_io, li.slottypes[i], true)
+            end
             print(emph_io, '\n')
         end
         print(emph_io, "\nBody:\n  ")
-        show_unquoted(emph_io, ast.args[3], 2)
+        body = Expr(:body); body.args = uncompressed_ast(li)
+        body.typ = li.rettype
+        show_unquoted(emph_io, body, 2)
         print(emph_io, '\n')
     end
     nothing
diff --git a/base/methodshow.jl b/base/methodshow.jl
index 9b9b52eaaa24d..5ebb194722512 100644
--- a/base/methodshow.jl
+++ b/base/methodshow.jl
@@ -34,8 +34,7 @@ function arg_decl_parts(m::Method)
         tv = Any[tv...]
     end
     li = m.func
-    e = uncompressed_ast(li)
-    argnames = e.args[1]
+    argnames = li.slotnames[1:li.nargs]
     s = symbol("?")
     decls = [argtype_decl(:tvar_env => tv, get(argnames,i,s), m.sig.parameters[i])
                 for i = 1:length(m.sig.parameters)]
@@ -49,11 +48,7 @@ function kwarg_decl(m::Method, kwtype::DataType)
     while d !== nothing
         if typeseq(d.sig, sig)
             li = d.func
-            e = uncompressed_ast(li)
-            argnames = Any[(isa(n,Expr) ? n.args[1] : n) for n in e.args[1]]
-            kwargs = filter!(x->!(x in argnames || '#' in string(x)),
-                Any[x[1] for x in e.args[2][1]])
-            return kwargs
+            return filter(x->!('#' in string(x)), li.slotnames[li.nargs+1:end])
         end
         d = d.next
     end
diff --git a/base/reflection.jl b/base/reflection.jl
index 633fd41f937da..f9c7ffa9cf693 100644
--- a/base/reflection.jl
+++ b/base/reflection.jl
@@ -170,7 +170,7 @@ end
 
 tt_cons(t::ANY, tup::ANY) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...})
 
-code_lowered(f, t::ANY=Tuple) = map(m->uncompressed_ast(m.func), methods(f, t))
+code_lowered(f, t::ANY=Tuple) = map(m->m.func, methods(f, t))
 function methods(f::ANY,t::ANY)
     if isa(f,Builtin)
         throw(ArgumentError("argument is not a generic function"))
@@ -249,7 +249,7 @@ done(mt::MethodTable, m::Method) = false
 done(mt::MethodTable, i::Void) = true
 
 uncompressed_ast(l::LambdaInfo) =
-    isa(l.ast,Expr) ? l.ast : ccall(:jl_uncompress_ast, Any, (Any,Any), l, l.ast)
+    isa(l.code,Array{Any,1}) ? l.code : ccall(:jl_uncompress_ast, Any, (Any,Any), l, l.code)
 
 # Printing code representations in IR and assembly
 function _dump_function(f, t::ANY, native, wrapper, strip_ir_metadata, dump_module)
@@ -295,14 +295,11 @@ function code_typed(f::ANY, types::ANY=Tuple; optimize=true)
     for x in _methods(f,types,-1)
         linfo = func_for_method_checked(x, types)
         if optimize
-            (tree, ty) = Core.Inference.typeinf(linfo, x[1], x[2], true)
+            (li, ty) = Core.Inference.typeinf(linfo, x[1], x[2], true)
         else
-            (tree, ty) = Core.Inference.typeinf_uncached(linfo, x[1], x[2], optimize=false)
+            (li, ty) = Core.Inference.typeinf_uncached(linfo, x[1], x[2], optimize=false)
         end
-        if !isa(tree, Expr)
-            tree = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, tree)
-        end
-        push!(asts, tree)
+        push!(asts, li)
     end
     asts
 end
@@ -312,7 +309,7 @@ function return_types(f::ANY, types::ANY=Tuple)
     rt = []
     for x in _methods(f,types,-1)
         linfo = func_for_method_checked(x,types)
-        (tree, ty) = Core.Inference.typeinf(linfo, x[1], x[2])
+        (_li, ty) = Core.Inference.typeinf(linfo, x[1], x[2])
         push!(rt, ty)
     end
     rt
diff --git a/base/serialize.jl b/base/serialize.jl
index a43320b237513..27a21e4fd3c56 100644
--- a/base/serialize.jl
+++ b/base/serialize.jl
@@ -310,6 +310,10 @@ function serialize(s::SerializationState, linfo::LambdaInfo)
     writetag(s.io, LAMBDASTATICDATA_TAG)
     serialize(s, object_number(linfo))
     serialize(s, uncompressed_ast(linfo))
+    serialize(s, linfo.slotnames)
+    serialize(s, linfo.slottypes)
+    serialize(s, linfo.slotflags)
+    serialize(s, linfo.gensymtypes)
     if isdefined(linfo.def, :roots)
         serialize(s, linfo.def.roots::Vector{Any})
     else
@@ -323,6 +327,8 @@ function serialize(s::SerializationState, linfo::LambdaInfo)
     serialize(s, linfo.file)
     serialize(s, linfo.line)
     serialize(s, linfo.pure)
+    serialize(s, linfo.nargs)
+    serialize(s, linfo.isva)
 end
 
 function serialize(s::SerializationState, t::Task)
@@ -552,7 +558,11 @@ function deserialize(s::SerializationState, ::Type{LambdaInfo})
         makenew = true
     end
     deserialize_cycle(s, linfo)
-    ast = deserialize(s)::Expr
+    code = deserialize(s)
+    slotnames = deserialize(s)
+    slottypes = deserialize(s)
+    slotflags = deserialize(s)
+    gensymtypes = deserialize(s)
     roots = deserialize(s)::Vector{Any}
     sparam_syms = deserialize(s)::SimpleVector
     sparam_vals = deserialize(s)::SimpleVector
@@ -562,17 +572,26 @@ function deserialize(s::SerializationState, ::Type{LambdaInfo})
     file = deserialize(s)
     line = deserialize(s)
     pure = deserialize(s)
+    nargs = deserialize(s)
+    isva = deserialize(s)
     if makenew
-        linfo.module = mod
+        linfo.code = code
+        linfo.slotnames = slotnames
+        linfo.slottypes = slottypes
+        linfo.slotflags = slotflags
+        linfo.gensymtypes = gensymtypes
+        linfo.roots = roots
         linfo.sparam_syms = sparam_syms
         linfo.sparam_vals = sparam_vals
-        ccall(:jl_lambda_info_set_ast, Void, (Any, Any), linfo, ast)
         linfo.inferred = infr
-        linfo.roots = roots
+        linfo.module = mod
         linfo.name = name
         linfo.file = file
         linfo.line = line
         linfo.pure = pure
+        linfo.nargs = nargs
+        linfo.isva = isva
+        ccall(:jl_lambda_info_init_properties, Void, (Any,), linfo)
         known_object_data[lnumber] = linfo
     end
     return linfo
diff --git a/base/show.jl b/base/show.jl
index f37afb0c6dca7..236caeeb875ce 100644
--- a/base/show.jl
+++ b/base/show.jl
@@ -209,9 +209,10 @@ function show(io::IO, m::Module)
 end
 
 function show(io::IO, l::LambdaInfo)
-    print(io, "AST(")
-    show(io, uncompressed_ast(l))
-    print(io, ")")
+    println(io, "LambdaInfo for ", l.name)
+    body = Expr(:body); body.args = uncompressed_ast(l)
+    body.typ = l.rettype
+    show(io, body)
 end
 
 function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one,
diff --git a/base/stacktraces.jl b/base/stacktraces.jl
index a3a7a503a37ac..47db91d98a54e 100644
--- a/base/stacktraces.jl
+++ b/base/stacktraces.jl
@@ -165,8 +165,8 @@ function show_spec_linfo(io::IO, frame::StackFrame)
         print(io, frame.func !== empty_sym ? frame.func : "?")
     else
         linfo = get(frame.outer_linfo)
-        if isdefined(linfo, 8)
-            params = linfo.(#=specTypes=#8).parameters
+        if isdefined(linfo, :specTypes)
+            params = linfo.specTypes.parameters
             ft = params[1]
             if ft <: Function && isempty(ft.parameters) &&
                     isdefined(ft.name.module, ft.name.mt.name) &&
diff --git a/src/alloc.c b/src/alloc.c
index 12be244b90ada..71a49cea8d820 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -96,7 +96,7 @@ jl_sym_t *dot_sym;    jl_sym_t *newvar_sym;
 jl_sym_t *boundscheck_sym; jl_sym_t *inbounds_sym;
 jl_sym_t *copyast_sym; jl_sym_t *fastmath_sym;
 jl_sym_t *pure_sym; jl_sym_t *simdloop_sym;
-jl_sym_t *meta_sym;
+jl_sym_t *meta_sym; jl_sym_t *compiler_temp_sym;
 jl_sym_t *inert_sym; jl_sym_t *vararg_sym;
 jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym;
 
@@ -271,12 +271,24 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type)
     return jv;
 }
 
+JL_DLLEXPORT void jl_lambda_info_init_properties(jl_lambda_info_t *li)
+{
+    int i;
+    uint8_t called=0;
+    for(i=1; i < li->nargs && i <= 8; i++) {
+        jl_value_t *ai = jl_cellref(li->slotnames,i);
+        if (ai == (jl_value_t*)unused_sym) continue;
+        if (jl_array_uint8_ref(li->slotflags,i)&64)
+            called |= (1<<(i-1));
+    }
+    li->called = called;
+}
+
 JL_DLLEXPORT void jl_lambda_info_set_ast(jl_lambda_info_t *li, jl_value_t *ast)
 {
-    li->ast = ast; jl_gc_wb(li, ast);
-    if (!jl_is_expr(ast))
-        return;
+    assert(jl_is_expr(ast));
     jl_array_t *body = jl_lam_body((jl_expr_t*)ast)->args;
+    li->code = body; jl_gc_wb(li, li->code);
     if (has_meta(body, pure_sym))
         li->pure = 1;
     jl_value_t *body1 = skip_meta(body);
@@ -288,25 +300,38 @@ JL_DLLEXPORT void jl_lambda_info_set_ast(jl_lambda_info_t *li, jl_value_t *ast)
         li->file = (jl_sym_t*)jl_exprarg(body1, 1);
         li->line = jl_unbox_long(jl_exprarg(body1, 0));
     }
-    jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)li->ast);
-    jl_array_t *args = jl_lam_args((jl_expr_t*)li->ast);
-    li->nslots = jl_array_len(vis);
-    jl_value_t *gensym_types = jl_lam_gensyms((jl_expr_t*)li->ast);
-    li->ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_long(gensym_types));
+    jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)ast);
+    jl_array_t *args = jl_lam_args((jl_expr_t*)ast);
+    size_t nslots = jl_array_len(vis);
     size_t narg = jl_array_len(args);
-    uint8_t called=0;
-    int i, j=0;
-    for(i=1; i < narg && i <= 8; i++) {
-        jl_value_t *ai = jl_cellref(args,i);
-        if (ai == (jl_value_t*)unused_sym || !jl_is_symbol(ai)) continue;
-        jl_value_t *vj;
-        do {
-            vj = jl_cellref(vis, j++);
-        } while (jl_cellref(vj,0) != ai);
-        if (jl_unbox_long(jl_cellref(vj,2))&64)
-            called |= (1<<(i-1));
+    li->nargs = narg;
+    li->isva = narg>0 && jl_is_rest_arg(jl_cellref(args, narg-1));
+    jl_value_t *gensym_types = jl_lam_gensyms((jl_expr_t*)ast);
+    size_t ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_long(gensym_types));
+    li->slotnames = jl_alloc_cell_1d(nslots);
+    li->slottypes = jl_nothing;
+    li->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots);
+    li->gensymtypes = jl_box_long(ngensym);
+    int i;
+    for(i=0; i < nslots; i++) {
+        jl_value_t *vi = jl_cellref(vis, i);
+        jl_sym_t *name = (jl_sym_t*)jl_cellref(vi, 0);
+        assert(jl_is_symbol(name));
+        char *str = jl_symbol_name(name);
+        if (i > 0 && name != unused_sym) {
+            if (str[0] == '#') {
+                // convention for renamed variables: #...#original_name
+                char *nxt = strchr(str + 1, '#');
+                if (nxt)
+                    name = jl_symbol(nxt+1);
+                else if (str[1] == 's')  // compiler-generated temporaries, #sXXX
+                    name = compiler_temp_sym;
+            }
+        }
+        jl_cellset(li->slotnames, i, name);
+        jl_array_uint8_set(li->slotflags, i, jl_unbox_long(jl_cellref(vi, 2)));
     }
-    li->called = called;
+    jl_lambda_info_init_properties(li);
 }
 
 JL_DLLEXPORT
@@ -316,7 +341,9 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_
     jl_lambda_info_t *li =
         (jl_lambda_info_t*)newobj((jl_value_t*)jl_lambda_info_type,
                                   NWORDS(sizeof(jl_lambda_info_t)));
-    li->ast = ast;
+    li->code = NULL;
+    li->slotnames = li->slotflags = NULL;
+    li->slottypes = li->gensymtypes = NULL;
     li->rettype = (jl_value_t*)jl_any_type;
     li->file = null_sym;
     li->module = ctx;
@@ -343,15 +370,27 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_
     li->pure = 0;
     li->called = 0xff;
     li->needs_sparam_vals_ducttape = 2;
-    if (ast != NULL)
+    if (ast != NULL) {
+        JL_GC_PUSH1(&li);
         jl_lambda_info_set_ast(li, ast);
+        JL_GC_POP();
+    }
     return li;
 }
 
-jl_lambda_info_t *jl_copy_lambda_info(jl_lambda_info_t *linfo)
+JL_DLLEXPORT jl_lambda_info_t *jl_copy_lambda_info(jl_lambda_info_t *linfo)
 {
     jl_lambda_info_t *new_linfo =
-        jl_new_lambda_info(linfo->ast, linfo->sparam_syms, linfo->sparam_vals, linfo->module);
+        jl_new_lambda_info(NULL, linfo->sparam_syms, linfo->sparam_vals, linfo->module);
+    new_linfo->code = linfo->code;
+    new_linfo->slotnames = linfo->slotnames;
+    new_linfo->slottypes = linfo->slottypes;
+    new_linfo->slotflags = linfo->slotflags;
+    new_linfo->gensymtypes = linfo->gensymtypes;
+    new_linfo->called = linfo->called;
+    new_linfo->nargs = linfo->nargs;
+    new_linfo->isva = linfo->isva;
+    new_linfo->pure = linfo->pure;
     new_linfo->rettype = linfo->rettype;
     new_linfo->tfunc = linfo->tfunc;
     new_linfo->name = linfo->name;
@@ -679,6 +718,8 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super
             t = jl_int64_type;
         else if (!strcmp(jl_symbol_name((jl_sym_t*)name), "Bool"))
             t = jl_bool_type;
+        else if (!strcmp(jl_symbol_name((jl_sym_t*)name), "UInt8"))
+            t = jl_uint8_type;
     }
     if (t == NULL)
         t = jl_new_uninitialized_datatype(jl_svec_len(fnames), 2); // TODO
@@ -880,6 +921,9 @@ void jl_init_int32_int64_cache(void)
         boxed_gensym_cache[i] = jl_box32(jl_gensym_type, i);
 #endif
     }
+    for(i=0; i < 256; i++) {
+        boxed_uint8_cache[i] = jl_box8(jl_uint8_type, i);
+    }
 }
 
 void jl_init_box_caches(void)
@@ -887,7 +931,6 @@ void jl_init_box_caches(void)
     int64_t i;
     for(i=0; i < 256; i++) {
         boxed_int8_cache[i]  = jl_box8(jl_int8_type, i);
-        boxed_uint8_cache[i] = jl_box8(jl_uint8_type, i);
     }
     for(i=0; i < NBOX_C; i++) {
         boxed_int16_cache[i]  = jl_box16(jl_int16_type, i-NBOX_C/2);
diff --git a/src/ast.c b/src/ast.c
index ae8e1d47a83e8..dbafa41d894a8 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -486,7 +486,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo)
                 e = cdr_(e);
             }
             nli = jl_new_lambda_info((jl_value_t*)ex, tvars, jl_emptysvec, jl_current_module);
-            jl_preresolve_globals(nli->ast, nli);
+            jl_preresolve_globals((jl_value_t*)nli, nli);
             JL_GC_POP();
             return (jl_value_t*)nli;
         }
@@ -918,18 +918,6 @@ jl_array_t *jl_lam_args(jl_expr_t *l)
     return (jl_array_t*)ae;
 }
 
-jl_sym_t *jl_lam_argname(jl_lambda_info_t *li, int i)
-{
-    jl_expr_t *ast;
-    if (jl_is_expr(li->ast))
-        ast = (jl_expr_t*)li->ast;
-    else
-        ast = (jl_expr_t*)jl_uncompress_ast(li, li->ast);
-    // NOTE (gc root): `ast` is not rooted here, but jl_lam_args and jl_cellref
-    // do not allocate.
-    return (jl_sym_t*)jl_cellref(jl_lam_args(ast),i);
-}
-
 // get array of var info records (for args and locals)
 jl_array_t *jl_lam_vinfo(jl_expr_t *l)
 {
@@ -961,13 +949,6 @@ jl_expr_t *jl_lam_body(jl_expr_t *l)
     return (jl_expr_t*)be;
 }
 
-jl_sym_t *jl_decl_var(jl_value_t *ex)
-{
-    if (jl_is_symbol(ex)) return (jl_sym_t*)ex;
-    assert(jl_is_expr(ex));
-    return (jl_sym_t*)jl_exprarg(ex, 0);
-}
-
 JL_DLLEXPORT int jl_is_rest_arg(jl_value_t *ex)
 {
     if (!jl_is_expr(ex)) return 0;
@@ -1018,25 +999,6 @@ JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr)
     return expr;
 }
 
-// given a new lambda_info with static parameter values, make a copy
-// of the tree with declared types evaluated and static parameters passed
-// on to all enclosed functions.
-// this tree can then be further mutated by optimization passes.
-JL_DLLEXPORT jl_value_t *jl_prepare_ast(jl_lambda_info_t *li)
-{
-    jl_value_t *ast = li->ast;
-    if (ast == NULL) return NULL;
-    JL_GC_PUSH1(&ast);
-    if (!jl_is_expr(ast)) {
-        ast = jl_uncompress_ast(li, ast);
-    }
-    else {
-        ast = jl_copy_ast(ast);
-    }
-    JL_GC_POP();
-    return ast;
-}
-
 JL_DLLEXPORT int jl_is_operator(char *sym)
 {
     jl_ast_context_t *ctx = jl_ast_ctx_enter();
@@ -1088,6 +1050,15 @@ jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam)
             return expr;
         return jl_module_globalref(lam->module, (jl_sym_t*)expr);
     }
+    else if (jl_is_lambda_info(expr)) {
+        jl_array_t *exprs = ((jl_lambda_info_t*)expr)->code;
+        if (jl_typeis(exprs, jl_array_any_type)) {
+            size_t l = jl_array_len(exprs);
+            size_t i;
+            for(i=0; i < l; i++)
+                jl_cellset(exprs, i, jl_preresolve_globals(jl_cellref(exprs,i), lam));
+        }
+    }
     else if (jl_is_expr(expr)) {
         jl_expr_t *e = (jl_expr_t*)expr;
         if (e->head == lambda_sym) {
diff --git a/src/builtins.c b/src/builtins.c
index 5eca771687f09..e12cd4a1fbe1e 100644
--- a/src/builtins.c
+++ b/src/builtins.c
@@ -113,12 +113,6 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, jl_value_t *expec
 
 JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var)
 {
-    if (jl_symbol_name(var)[0] == '#') {
-        // convention for renamed variables: #...#original_name
-        char *nxt = strchr(jl_symbol_name(var) + 1, '#');
-        if (nxt)
-            var = jl_symbol(nxt+1);
-    }
     jl_throw(jl_new_struct(jl_undefvarerror_type, var));
 }
 
@@ -1084,11 +1078,11 @@ jl_value_t *jl_mk_builtin_func(const char *name, jl_fptr_t fptr)
 {
     jl_sym_t *sname = jl_symbol(name);
     jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0);
-    jl_lambda_info_t *li = jl_new_lambda_info(jl_nothing, jl_emptysvec, jl_emptysvec, jl_core_module);
+    jl_lambda_info_t *li = jl_new_lambda_info(NULL, jl_emptysvec, jl_emptysvec, jl_core_module);
     li->fptr = fptr;
     li->name = sname;
     // TODO jb/functions: what should li->ast be?
-    li->ast = (jl_value_t*)jl_exprn(lambda_sym,0); jl_gc_wb(li, li->ast);
+    li->code = (jl_array_t*)jl_an_empty_cell; jl_gc_wb(li, li->code);
     jl_method_cache_insert(jl_gf_mtable(f), jl_anytuple_type, li);
     return f;
 }
diff --git a/src/cgutils.cpp b/src/cgutils.cpp
index 6e4affa00f061..071adbb3243fc 100644
--- a/src/cgutils.cpp
+++ b/src/cgutils.cpp
@@ -849,9 +849,12 @@ static inline jl_module_t *topmod(jl_codectx_t *ctx)
 static jl_value_t *expr_type(jl_value_t *e, jl_codectx_t *ctx)
 {
     if (jl_is_gensym(e)) {
+        if (jl_is_long(ctx->linfo->gensymtypes))
+            return (jl_value_t*)jl_any_type;
         int idx = ((jl_gensym_t*)e)->id;
-        jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast);
-        return (jl_is_array(gensym_types) ? jl_cellref(gensym_types, idx) : (jl_value_t*)jl_any_type);
+        assert(jl_is_array(ctx->linfo->gensymtypes));
+        jl_array_t *gensym_types = (jl_array_t*)ctx->linfo->gensymtypes;
+        return jl_cellref(gensym_types, idx);
     }
     if (jl_typeis(e, jl_slot_type)) {
         jl_value_t *typ = jl_slot_get_type(e);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 416b3a9624d95..e9433b0867986 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -556,7 +556,6 @@ typedef struct {
     std::map<int, BasicBlock*> *labels;
     std::map<int, Value*> *handlers;
     jl_module_t *module;
-    jl_expr_t *ast;
     jl_lambda_info_t *linfo;
     Value *spvals_ptr;
     Value *argArray;
@@ -742,7 +741,7 @@ static bool store_unboxed_p(int s, jl_codectx_t *ctx)
 
 static jl_sym_t *slot_symbol(int s, jl_codectx_t *ctx)
 {
-    return (jl_sym_t*)jl_cellref(jl_cellref(jl_lam_vinfo(ctx->ast), s), 0);
+    return (jl_sym_t*)jl_cellref(ctx->linfo->slotnames, s);
 }
 
 static Value *alloc_local(int s, jl_codectx_t *ctx)
@@ -759,7 +758,8 @@ static Value *alloc_local(int s, jl_codectx_t *ctx)
     // CreateAlloca is OK here because alloc_local is only called during prologue setup
     Value *lv = builder.CreateAlloca(vtype, 0, jl_symbol_name(slot_symbol(s,ctx)));
     vi.value = mark_julia_slot(lv, jt);
-    vi.value.isimmutable &= vi.isSA; // slot is not immutable if there are multiple assignments
+    // slot is not immutable if there are multiple assignments
+    vi.value.isimmutable &= (vi.isSA && s >= ctx->linfo->nargs);
     assert(vi.value.isboxed == false);
     return lv;
 }
@@ -3018,7 +3018,7 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx)
         }
         if (slot.isboxed && slot.isimmutable) {
             // see if inference had a better type for the gensym than the expression (after inlining getfield on a Tuple)
-            jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast);
+            jl_value_t *gensym_types = (jl_value_t*)ctx->linfo->gensymtypes;
             if (jl_is_array(gensym_types)) {
                 jl_value_t *declType = jl_cellref(gensym_types, idx);
                 if (declType != slot.typ) {
@@ -3860,7 +3860,7 @@ static Function *gen_cfun_wrapper(jl_lambda_info_t *lam, jl_function_t *ff, jl_v
 }
 
 // generate a julia-callable function that calls f (AKA lam)
-static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Function *f, bool sret, Module *M)
+static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, Function *f, bool sret, Module *M)
 {
     std::stringstream funcName;
     const std::string &fname = f->getName().str();
@@ -3893,7 +3893,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct
     ctx.spvals_ptr = NULL;
     allocate_gc_frame(b0, &ctx);
 
-    size_t nargs = jl_array_dim0(jl_lam_args(ast));
+    size_t nargs = lam->nargs;
     size_t nfargs = f->getFunctionType()->getNumParams();
     Value **args = (Value**) alloca(nfargs*sizeof(Value*));
     unsigned idx = 0;
@@ -3945,12 +3945,10 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     assert(declarations && "Capturing declarations is always required");
 
     // step 1. unpack AST and allocate codegen context for this function
-    jl_expr_t *ast = (jl_expr_t*)lam->ast;
-    JL_GC_PUSH1(&ast);
-    if (!jl_is_expr(ast)) {
-        ast = (jl_expr_t*)jl_uncompress_ast(lam, (jl_value_t*)ast);
-    }
-    assert(jl_is_expr(ast));
+    jl_array_t *code = lam->code;
+    JL_GC_PUSH1(&code);
+    if (!jl_typeis(code,jl_array_any_type))
+        code = jl_uncompress_ast(lam, code);
     //jl_static_show(JL_STDOUT, (jl_value_t*)ast);
     //jl_printf(JL_STDOUT, "\n");
     std::map<int, jl_arrayvar_t> arrayvars;
@@ -3961,7 +3959,6 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     ctx.labels = &labels;
     ctx.handlers = &handlers;
     ctx.module = lam->module;
-    ctx.ast = ast;
     ctx.linfo = lam;
     ctx.funcName = jl_symbol_name(lam->name);
     ctx.vaSlot = -1;
@@ -3971,12 +3968,9 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     ctx.spvals_ptr = NULL;
 
     // step 2. process var-info lists to see what vars need boxing
-    jl_value_t *gensym_types = jl_lam_gensyms(ast);
-    int n_gensyms = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_gensym(gensym_types));
-    jl_array_t *largs = jl_lam_args(ast);
-    size_t largslen = jl_array_dim0(largs);
-    jl_array_t *vinfos = jl_lam_vinfo(ast);
-    size_t vinfoslen = jl_array_dim0(vinfos);
+    int n_gensyms = jl_is_long(lam->gensymtypes) ? jl_unbox_long(lam->gensymtypes) : jl_array_len(lam->gensymtypes);
+    size_t largslen = lam->nargs;
+    size_t vinfoslen = jl_array_dim0(lam->slotnames);
     ctx.slots.resize(vinfoslen);
     size_t nreq = largslen;
     int va = 0;
@@ -3986,10 +3980,10 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
                             // compiling this would cause all specializations to inherit
                             // this code and could create an broken compile / function cache
 
-    if (nreq > 0 && jl_is_rest_arg(jl_cellref(largs,largslen-1))) {
+    if (nreq > 0 && lam->isva) {
         nreq--;
         va = 1;
-        jl_sym_t *vn = jl_decl_var(jl_cellref(largs,largslen-1));
+        jl_sym_t *vn = (jl_sym_t*)jl_cellref(lam->slotnames,largslen-1);
         if (vn != unused_sym)
             ctx.vaSlot = largslen-1;
     }
@@ -4002,8 +3996,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     // step 3. some variable analysis
     size_t i;
     for(i=0; i < nreq; i++) {
-        jl_value_t *arg = jl_cellref(largs,i);
-        jl_sym_t *argname = jl_decl_var(arg);
+        jl_sym_t *argname = (jl_sym_t*)jl_cellref(lam->slotnames,i);
         if (argname == unused_sym) continue;
         jl_varinfo_t &varinfo = ctx.slots[i];
         varinfo.isArgument = true;
@@ -4017,28 +4010,28 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     }
 
     for(i=0; i < vinfoslen; i++) {
-        jl_array_t *vi = (jl_array_t*)jl_cellref(vinfos, i);
-        assert(jl_is_array(vi));
-        jl_sym_t *vname = ((jl_sym_t*)jl_cellref(vi,0));
-        assert(jl_is_symbol(vname));
         jl_varinfo_t &varinfo = ctx.slots[i];
-        varinfo.isAssigned = (jl_vinfo_assigned(vi)!=0);
+        uint8_t flags = jl_array_uint8_ref(lam->slotflags, i);
+        varinfo.isAssigned = (jl_vinfo_assigned(flags)!=0);
         varinfo.escapes = false;
-        varinfo.isSA = (jl_vinfo_sa(vi)!=0);
-        varinfo.usedUndef = (jl_vinfo_usedundef(vi)!=0) || (!varinfo.isArgument && !lam->inferred);
+        varinfo.isSA = (jl_vinfo_sa(flags)!=0);
+        varinfo.usedUndef = (jl_vinfo_usedundef(flags)!=0) || (!varinfo.isArgument && !lam->inferred);
         if (!varinfo.isArgument || varinfo.isAssigned) {
-            jl_value_t *typ = jl_cellref(vi,1);
+            jl_value_t *typ = jl_is_array(lam->slottypes) ? jl_cellref(lam->slottypes,i) : (jl_value_t*)jl_any_type;
             if (!jl_is_type(typ))
                 typ = (jl_value_t*)jl_any_type;
             varinfo.value = mark_julia_type((Value*)NULL, false, typ, &ctx);
         }
     }
 
+    jl_array_t *stmts = code;
+    size_t stmtslen = jl_array_dim0(stmts);
+
     // finish recording escape info
-    simple_escape_analysis((jl_value_t*)ast, true, &ctx);
+    for(i=0; i < stmtslen; i++)
+        simple_escape_analysis(jl_cellref(stmts,i), true, &ctx);
 
     // determine which vars need to be volatile
-    jl_array_t *stmts = jl_lam_body(ast)->args;
     mark_volatile_vars(stmts, ctx.slots);
 
     // step 4. determine function signature
@@ -4106,7 +4099,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
 #ifdef LLVM37
         f->addFnAttr("no-frame-pointer-elim", "true");
 #endif
-        fwrap = gen_jlcall_wrapper(lam, ast, f, ctx.sret, M);
+        fwrap = gen_jlcall_wrapper(lam, f, ctx.sret, M);
         declarations->functionObject = function_proto(fwrap);
         declarations->specFunctionObject = function_proto(f);
     }
@@ -4283,7 +4276,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
         const bool AlwaysPreserve = true;
         // Go over all arguments and local variables and initialize their debug information
         for(i=0; i < nreq; i++) {
-            jl_sym_t *argname = jl_decl_var(jl_cellref(largs,i));
+            jl_sym_t *argname = (jl_sym_t*)jl_cellref(lam->slotnames,i);
             if (argname == unused_sym) continue;
             jl_varinfo_t &varinfo = ctx.slots[i];
 #ifdef LLVM38
@@ -4335,7 +4328,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
 #endif
         }
         for(i=0; i < vinfoslen; i++) {
-            jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0);
+            jl_sym_t *s = (jl_sym_t*)jl_cellref(lam->slotnames,i);
             jl_varinfo_t &varinfo = ctx.slots[i];
             if (varinfo.isArgument)
                 continue;
@@ -4397,7 +4390,6 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     allocate_gc_frame(b0, &ctx);
 
     // step 8. allocate space for exception handler contexts
-    size_t stmtslen = jl_array_dim0(stmts);
     for(i=0; i < stmtslen; i++) {
         jl_value_t *stmt = jl_cellref(stmts,i);
         if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == enter_sym) {
@@ -4416,7 +4408,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
 
     // get pointers for locals stored in the gc frame array (argTemp)
     for(i=0; i < vinfoslen; i++) {
-        jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0);
+        jl_sym_t *s = slot_symbol(i, &ctx);
         if (s == unused_sym) continue;
         jl_varinfo_t &varinfo = ctx.slots[i];
         assert(!varinfo.memloc); // variables shouldn't also have memory locs already
@@ -4450,7 +4442,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
         if (varinfo.isAssigned || // always need a slot if the variable is assigned
             specsig || // for arguments, give them stack slots if then aren't in `argArray` (otherwise, will use that pointer)
             (va && (int)i == ctx.vaSlot && varinfo.escapes) || // or it's the va arg tuple
-            (s != unused_sym && s == jl_decl_var(jl_cellref(largs, 0)))) { // or it is the first argument (which isn't in `argArray`)
+            (s != unused_sym && i == 0)) { // or it is the first argument (which isn't in `argArray`)
             AllocaInst *av = new AllocaInst(T_pjlvalue, jl_symbol_name(s), /*InsertBefore*/ctx.ptlsStates);
             varinfo.memloc = av;
 #ifdef LLVM36
@@ -4480,7 +4472,7 @@ static std::unique_ptr<Module> emit_function(jl_lambda_info_t *lam, jl_llvm_func
     if (ctx.sret)
         AI++; // skip sret slot
     for(i=0; i < nreq; i++) {
-        jl_sym_t *s = jl_decl_var(jl_cellref(largs,i));
+        jl_sym_t *s = (jl_sym_t*)jl_cellref(lam->slotnames,i);
         jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i);
         bool isboxed;
         Type *llvmArgType = julia_type_to_llvm(argType, &isboxed);
diff --git a/src/dump.c b/src/dump.c
index 54447cd374fcb..8a5d4ae0fe46f 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -515,6 +515,8 @@ static void jl_serialize_datatype(ios_t *s, jl_datatype_t *dt)
         tag = 3;
     else if (dt == jl_int64_type)
         tag = 4;
+    else if (dt == jl_uint8_type)
+        tag = 8;
     writetag(s, (jl_value_t*)SmallDataType_tag);
     write_uint8(s, 0); // virtual size
     jl_serialize_value(s, (jl_value_t*)jl_datatype_type);
@@ -608,14 +610,6 @@ static void jl_serialize_module(ios_t *s, jl_module_t *m)
 
 static int is_ast_node(jl_value_t *v)
 {
-    if (jl_is_lambda_info(v)) {
-        jl_lambda_info_t *li = (jl_lambda_info_t*)v;
-        if (jl_is_expr(li->ast)) {
-            li->ast = jl_compress_ast(li, li->ast);
-            jl_gc_wb(li, li->ast);
-        }
-        return 0;
-    }
     return jl_is_symbol(v) || jl_is_slot(v) || jl_is_gensym(v) ||
         jl_is_expr(v) || jl_is_newvarnode(v) || jl_is_svec(v) ||
         jl_typeis(v, jl_array_any_type) || jl_is_tuple(v) ||
@@ -791,7 +785,11 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v)
     else if (jl_is_lambda_info(v)) {
         writetag(s, jl_lambda_info_type);
         jl_lambda_info_t *li = (jl_lambda_info_t*)v;
-        jl_serialize_value(s, li->ast);
+        jl_serialize_value(s, li->code);
+        jl_serialize_value(s, li->slotnames);
+        jl_serialize_value(s, li->slottypes);
+        jl_serialize_value(s, li->slotflags);
+        jl_serialize_value(s, li->gensymtypes);
         jl_serialize_value(s, li->rettype);
         jl_serialize_value(s, (jl_value_t*)li->sparam_syms);
         jl_serialize_value(s, (jl_value_t*)li->sparam_vals);
@@ -810,10 +808,10 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v)
                 for(i=0; i < l; i += 3) {
                     if (!jl_is_leaf_type(jl_cellref(tf,i))) {
                         jl_value_t *ret = jl_cellref(tf,i+1);
-                        if (jl_is_tuple(ret)) {
-                            jl_value_t *ast = jl_fieldref(ret, 0);
-                            if (jl_is_array(ast) && jl_array_len(ast) > 500)
-                                jl_cellset(tf, i+1, jl_fieldref(ret,1));
+                        if (jl_is_lambda_info(ret)) {
+                            jl_array_t *code = ((jl_lambda_info_t*)ret)->code;
+                            if (jl_is_array(code) && jl_array_len(code) > 500)
+                                jl_cellset(tf, i+1, ((jl_lambda_info_t*)ret)->rettype);
                         }
                     }
                 }
@@ -826,10 +824,10 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v)
         write_int8(s, li->inferred);
         write_int8(s, li->pure);
         write_int8(s, li->called);
+        write_int8(s, li->isva);
         jl_serialize_value(s, (jl_value_t*)li->file);
         write_int32(s, li->line);
-        write_int32(s, li->nslots);
-        write_int32(s, li->ngensym);
+        write_int32(s, li->nargs);
         jl_serialize_value(s, (jl_value_t*)li->module);
         jl_serialize_value(s, (jl_value_t*)li->roots);
         jl_serialize_value(s, (jl_value_t*)li->def);
@@ -1186,6 +1184,8 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc)
         dt = jl_bool_type;
     else if (tag == 4)
         dt = jl_int64_type;
+    else if (tag == 8)
+        dt = jl_uint8_type;
     else
         dt = jl_new_uninitialized_datatype(nf, fielddesc_type);
     assert(tree_literal_values==NULL && mode != MODE_AST);
@@ -1422,8 +1422,11 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t
                                       NWORDS(sizeof(jl_lambda_info_t)));
         if (usetable)
             arraylist_push(&backref_list, li);
-        li->ast = jl_deserialize_value(s, &li->ast);
-        jl_gc_wb(li, li->ast);
+        li->code = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->code); jl_gc_wb(li, li->code);
+        li->slotnames = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->slotnames); jl_gc_wb(li, li->slotnames);
+        li->slottypes = jl_deserialize_value(s, &li->slottypes); jl_gc_wb(li, li->slottypes);
+        li->slotflags = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->slotflags); jl_gc_wb(li, li->slotflags);
+        li->gensymtypes = jl_deserialize_value(s, &li->gensymtypes); jl_gc_wb(li, li->gensymtypes);
         li->rettype = jl_deserialize_value(s, &li->rettype);
         jl_gc_wb(li, li->rettype);
         li->sparam_syms = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&li->sparam_syms);
@@ -1441,11 +1444,11 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t
         li->inferred = read_int8(s);
         li->pure = read_int8(s);
         li->called = read_int8(s);
+        li->isva = read_int8(s);
         li->file = (jl_sym_t*)jl_deserialize_value(s, NULL);
         jl_gc_wb(li, li->file);
         li->line = read_int32(s);
-        li->nslots = read_int32(s);
-        li->ngensym = read_int32(s);
+        li->nargs = read_int32(s);
         li->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&li->module);
         jl_gc_wb(li, li->module);
         li->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->roots);
@@ -2032,7 +2035,7 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len)
     JL_SIGATOMIC_END();
 }
 
-JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast)
+JL_DLLEXPORT jl_array_t *jl_compress_ast(jl_lambda_info_t *li, jl_array_t *ast)
 {
     JL_SIGATOMIC_BEGIN();
     JL_LOCK(dump); // Might GC
@@ -2054,7 +2057,7 @@ JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast)
 
     //jl_printf(JL_STDERR, "%d bytes, %d values\n", dest.size, vals->length);
 
-    jl_value_t *v = (jl_value_t*)jl_takebuf_array(&dest);
+    jl_array_t *v = jl_takebuf_array(&dest);
     if (jl_array_len(tree_literal_values) == 0 && last_tlv == NULL) {
         li->def->roots = NULL;
     }
@@ -2067,7 +2070,7 @@ JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast)
     return v;
 }
 
-JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data)
+JL_DLLEXPORT jl_array_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_array_t *data)
 {
     JL_SIGATOMIC_BEGIN();
     JL_LOCK(dump); // Might GC
@@ -2082,7 +2085,7 @@ JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *dat
     ios_setbuf(&src, (char*)bytes->data, jl_array_len(bytes), 0);
     src.size = jl_array_len(bytes);
     int en = jl_gc_enable(0); // Might GC
-    jl_value_t *v = jl_deserialize_value(&src, NULL);
+    jl_array_t *v = (jl_array_t*)jl_deserialize_value(&src, NULL);
     jl_gc_enable(en);
     tree_literal_values = NULL;
     tree_enclosing_module = NULL;
@@ -2400,7 +2403,7 @@ void jl_init_serializer(void)
                      jl_box_int32(39), jl_box_int32(40), jl_box_int32(41),
                      jl_box_int32(42), jl_box_int32(43), jl_box_int32(44),
                      jl_box_int32(45), jl_box_int32(46), jl_box_int32(47),
-                     jl_box_int32(48), jl_box_int32(49), jl_box_int32(50),
+                     jl_box_int32(48), jl_box_int32(49),
 #endif
                      jl_box_int64(0), jl_box_int64(1), jl_box_int64(2),
                      jl_box_int64(3), jl_box_int64(4), jl_box_int64(5),
@@ -2419,7 +2422,7 @@ void jl_init_serializer(void)
                      jl_box_int64(39), jl_box_int64(40), jl_box_int64(41),
                      jl_box_int64(42), jl_box_int64(43), jl_box_int64(44),
                      jl_box_int64(45), jl_box_int64(46), jl_box_int64(47),
-                     jl_box_int64(48), jl_box_int64(49), jl_box_int64(50),
+                     jl_box_int64(48), jl_box_int64(49),
 #endif
                      jl_labelnode_type, jl_linenumbernode_type,
                      jl_gotonode_type, jl_quotenode_type, jl_topnode_type,
@@ -2431,7 +2434,7 @@ void jl_init_serializer(void)
                      jl_ANY_flag, jl_array_any_type, jl_intrinsic_type, jl_method_type,
                      jl_methtable_type, jl_voidpointer_type, jl_newvarnode_type,
                      jl_array_symbol_type, jl_anytuple_type, jl_tparam0(jl_anytuple_type),
-                     jl_typeof(jl_emptytuple),
+                     jl_typeof(jl_emptytuple), jl_array_uint8_type,
                      jl_symbol_type->name, jl_gensym_type->name, jl_tuple_typename,
                      jl_ref_type->name, jl_pointer_type->name, jl_simplevector_type->name,
                      jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name,
diff --git a/src/gf.c b/src/gf.c
index 880c807f26d63..ad29e0ed625ad 100644
--- a/src/gf.c
+++ b/src/gf.c
@@ -293,7 +293,7 @@ static jl_lambda_info_t *jl_add_static_parameters(jl_lambda_info_t *l, jl_svec_t
       for example, if an unspecialized method is needed,
       the slow compiled code should be associated with
       method->unspecialized, not method */
-    assert(!nli->ast ||
+    assert(!nli->code ||
            (nli->fptr == NULL &&
             nli->jlcall_api == 0 &&
             nli->functionObjects.functionObject == NULL &&
@@ -330,11 +330,18 @@ jl_lambda_info_t *jl_get_unspecialized(jl_lambda_info_t *method)
         return method->unspecialized;
     if (method->specTypes && def->sparam_syms != jl_emptysvec) {
         if (def->needs_sparam_vals_ducttape == 2) {
-            jl_value_t *code = method->ast;
+            jl_array_t *code = method->code;
             JL_GC_PUSH1(&code);
-            if (!jl_is_expr(code))
+            if (!jl_typeis(code, jl_array_any_type))
                 code = jl_uncompress_ast(def, code);
-            def->needs_sparam_vals_ducttape = jl_has_intrinsics(method, (jl_expr_t*)code, method->module);
+            size_t i, l = jl_array_len(code);
+            def->needs_sparam_vals_ducttape = 0;
+            for(i=0; i < l; i++) {
+                if (jl_has_intrinsics(method, jl_cellref(code,i), method->module)) {
+                    def->needs_sparam_vals_ducttape = 1;
+                    break;
+                }
+            }
             JL_GC_POP();
         }
         if (method->needs_sparam_vals_ducttape) {
@@ -815,7 +822,7 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type,
     else
         (void)jl_method_cache_insert(mt, type, newmeth);
 
-    if (newmeth->ast != NULL) {
+    if (newmeth->code != NULL) {
         jl_array_t *spe = method->specializations;
         if (spe == NULL) {
             spe = jl_alloc_cell_1d(1);
@@ -895,11 +902,12 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator
 
     ex = jl_exprn(lambda_sym, 2);
 
-    jl_expr_t *generatorast = (jl_expr_t*)generator->ast;
-    if (!jl_is_expr(generatorast))
-        generatorast = (jl_expr_t*)jl_uncompress_ast(generator, (jl_value_t*)generatorast);
-    jl_array_t *argnames = jl_lam_args(generatorast);
+    int nargs = generator->nargs;
+    jl_array_t *argnames = jl_alloc_cell_1d(nargs);
     jl_cellset(ex->args, 0, argnames);
+    size_t i;
+    for(i=0; i < nargs; i++)
+        jl_cellset(argnames, i, jl_cellref(generator->slotnames,i));
 
     jl_expr_t *scopeblock = jl_exprn(jl_symbol("scope-block"), 1);
     jl_cellset(ex->args, 1, scopeblock);
@@ -913,8 +921,7 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator
 
     // invoke code generator
     assert(jl_nparams(tt) == jl_array_len(argnames) ||
-           (jl_is_rest_arg(jl_cellref(argnames, jl_array_len(argnames)-1)) &&
-            (jl_nparams(tt) >= jl_array_len(argnames) - 1)));
+           (generator->isva && (jl_nparams(tt) >= jl_array_len(argnames) - 1)));
     jl_cellset(body->args, 1, jl_call_unspecialized(sparam_vals, generator, jl_svec_data(tt->parameters), jl_nparams(tt)));
 
     if (generator->sparam_syms != jl_emptysvec) {
@@ -930,6 +937,8 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator
     // need to eval macros in the right module, but not give a warning for the `eval` call unless that results in a call to `eval`
     jl_lambda_info_t *func = (jl_lambda_info_t*)jl_toplevel_eval_in_warn(generator->module, (jl_value_t*)ex, 1);
     func->name = generator->name;
+    if (generator->isva)
+        func->isva = 1;
     JL_GC_POP();
     return func;
 }
@@ -1446,7 +1455,7 @@ jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types)
     } JL_CATCH {
         goto not_found;
     }
-    if (sf == NULL || sf->ast == NULL || sf->inInference)
+    if (sf == NULL || sf->code == NULL || sf->inInference)
         goto not_found;
     if (sf->functionObjects.functionObject == NULL) {
         if (sf->fptr != NULL)
diff --git a/src/init.c b/src/init.c
index 90166c4e4ed7a..aa92a14cb4fa5 100644
--- a/src/init.c
+++ b/src/init.c
@@ -808,7 +808,6 @@ void jl_get_builtin_hooks(void)
 
     jl_char_type    = (jl_datatype_t*)core("Char");
     jl_int8_type    = (jl_datatype_t*)core("Int8");
-    jl_uint8_type   = (jl_datatype_t*)core("UInt8");
     jl_int16_type   = (jl_datatype_t*)core("Int16");
     jl_uint16_type  = (jl_datatype_t*)core("UInt16");
     jl_uint32_type  = (jl_datatype_t*)core("UInt32");
@@ -841,9 +840,6 @@ void jl_get_builtin_hooks(void)
     jl_ascii_string_type = (jl_datatype_t*)core("ASCIIString");
     jl_utf8_string_type = (jl_datatype_t*)core("UTF8String");
     jl_weakref_type = (jl_datatype_t*)core("WeakRef");
-
-    jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type,
-                                        jl_svec2(jl_uint8_type, jl_box_long(1)));
 }
 
 JL_DLLEXPORT void jl_get_system_hooks(void)
diff --git a/src/interpreter.c b/src/interpreter.c
index 246529dad632c..1e7ccd285fcd0 100644
--- a/src/interpreter.c
+++ b/src/interpreter.c
@@ -112,7 +112,17 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super)
     jl_gc_wb(tt, tt->super);
 }
 
-    static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam)
+static int jl_linfo_nslots(jl_lambda_info_t *li)
+{
+    return jl_array_len(li->slotflags);
+}
+
+static int jl_linfo_ngensyms(jl_lambda_info_t *li)
+{
+    return jl_is_long(li->gensymtypes) ? jl_unbox_long(li->gensymtypes) : jl_array_len(li->gensymtypes);
+}
+
+static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam)
 {
     if (jl_is_symbol(e)) {
         jl_value_t *v = jl_get_global(jl_current_module, (jl_sym_t*)e);
@@ -122,10 +132,10 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super)
     }
     if (jl_is_gensym(e)) {
         ssize_t genid = ((jl_gensym_t*)e)->id;
-        if (genid >= lam->ngensym || genid < 0)
+        if (genid >= jl_linfo_ngensyms(lam) || genid < 0)
             jl_error("access to invalid GenSym location");
         else
-            return locals[lam->nslots + genid];
+            return locals[jl_linfo_nslots(lam) + genid];
     }
     if (jl_is_quotenode(e)) {
         return jl_fieldref(e,0);
@@ -140,11 +150,11 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super)
     if (!jl_is_expr(e)) {
         if (jl_typeis(e, jl_slot_type)) {
             ssize_t n = jl_slot_number(e);
-            if (n > lam->nslots || n < 1)
+            if (n > jl_linfo_nslots(lam) || n < 1)
                 jl_error("access to invalid slot number");
             jl_value_t *v = locals[n-1];
-            if (v == NULL) // TODO get var name from `lam`
-                jl_undefined_var_error(anonymous_sym);
+            if (v == NULL)
+                jl_undefined_var_error((jl_sym_t*)jl_cellref(lam->slotnames,n-1));
             return v;
         }
         if (jl_is_globalref(e)) {
@@ -158,7 +168,7 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super)
             jl_value_t *var = jl_fieldref(e,0);
             assert(jl_typeis(var,jl_slot_type));
             ssize_t n = jl_slot_number(var);
-            assert(n <= lam->nslots && n > 0);
+            assert(n <= jl_linfo_nslots(lam) && n > 0);
             locals[n-1] = NULL;
             return (jl_value_t*)jl_nothing;
         }
@@ -175,13 +185,13 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super)
         jl_value_t *rhs = eval(args[1], locals, lam);
         if (jl_is_gensym(sym)) {
             ssize_t genid = ((jl_gensym_t*)sym)->id;
-            if (genid >= lam->ngensym || genid < 0)
+            if (genid >= jl_linfo_ngensyms(lam) || genid < 0)
                 jl_error("assignment to invalid GenSym location");
-            locals[lam->nslots + genid] = rhs;
+            locals[jl_linfo_nslots(lam) + genid] = rhs;
         }
         else if (jl_typeis(sym,jl_slot_type)) {
             ssize_t n = jl_slot_number(sym);
-            assert(n <= lam->nslots && n > 0);
+            assert(n <= jl_linfo_nslots(lam) && n > 0);
             locals[n-1] = rhs;
         }
         else {
@@ -520,10 +530,9 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, jl_lambda_i
 
 jl_value_t *jl_interpret_toplevel_thunk(jl_lambda_info_t *lam)
 {
-    jl_expr_t *ast = (jl_expr_t*)lam->ast;
-    jl_array_t *stmts = jl_lam_body(ast)->args;
+    jl_array_t *stmts = lam->code;
     jl_value_t **locals;
-    JL_GC_PUSHARGS(locals, lam->nslots + lam->ngensym);
+    JL_GC_PUSHARGS(locals, jl_linfo_nslots(lam) + jl_linfo_ngensyms(lam));
     jl_value_t *r = eval_body(stmts, locals, lam, 0, 1);
     JL_GC_POP();
     return r;
diff --git a/src/jltypes.c b/src/jltypes.c
index 1bc170ca3ee8b..d974caab9aadd 100644
--- a/src/jltypes.c
+++ b/src/jltypes.c
@@ -3370,6 +3370,10 @@ void jl_init_types(void)
     jl_int64_type = jl_new_bitstype((jl_value_t*)jl_symbol("Int64"),
                                     jl_any_type, jl_emptysvec, 64);
 
+    jl_uint8_type = NULL;
+    jl_uint8_type = jl_new_bitstype((jl_value_t*)jl_symbol("UInt8"),
+                                    jl_any_type, jl_emptysvec, 8);
+
     jl_gensym_type = jl_new_datatype(jl_symbol("GenSym"), jl_any_type, jl_emptysvec,
                                      jl_svec1(jl_symbol("id")),
                                      jl_svec1(jl_long_type), 0, 0, 1);
@@ -3431,6 +3435,9 @@ void jl_init_types(void)
                                    jl_svec(2, jl_symbol_type,
                                             jl_box_long(1)));
 
+    jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type,
+                                        jl_svec2(jl_uint8_type, jl_box_long(1)));
+
     jl_expr_type =
         jl_new_datatype(jl_symbol("Expr"),
                         jl_any_type, jl_emptysvec,
@@ -3486,7 +3493,9 @@ void jl_init_types(void)
     jl_lambda_info_type =
         jl_new_datatype(jl_symbol("LambdaInfo"),
                         jl_any_type, jl_emptysvec,
-                        jl_svec(19, jl_symbol("ast"), jl_symbol("rettype"),
+                        jl_svec(23, jl_symbol("code"), jl_symbol("slotnames"),
+                                jl_symbol("slottypes"), jl_symbol("slotflags"),
+                                jl_symbol("gensymtypes"), jl_symbol("rettype"),
                                 jl_symbol("sparam_syms"), jl_symbol("sparam_vals"),
                                 jl_symbol("tfunc"), jl_symbol("name"),
                                 jl_symbol("roots"),
@@ -3495,20 +3504,21 @@ void jl_init_types(void)
                                 jl_symbol("specializations"),
                                 jl_symbol("module"), jl_symbol("def"),
                                 jl_symbol("file"), jl_symbol("line"),
-                                jl_symbol("nslots"), jl_symbol("ngensym"),
-                                jl_symbol("inferred"),
-                                jl_symbol("pure"),
+                                jl_symbol("nargs"), jl_symbol("inferred"),
+                                jl_symbol("pure"), jl_symbol("isva"),
                                 jl_symbol("inInference")),
-                        jl_svec(19, jl_any_type, jl_any_type,
+                        jl_svec(23, jl_any_type, jl_array_any_type,
+                                jl_any_type, jl_array_uint8_type,
+                                jl_any_type, jl_any_type,
                                 jl_simplevector_type, jl_simplevector_type,
                                 jl_any_type, jl_sym_type,
                                 jl_any_type, jl_any_type,
                                 jl_any_type, jl_array_any_type,
                                 jl_module_type, jl_any_type,
                                 jl_sym_type, jl_int32_type,
-                                jl_int32_type, jl_int32_type,
+                                jl_int32_type, jl_bool_type,
                                 jl_bool_type, jl_bool_type, jl_bool_type),
-                        0, 1, 5);
+                        0, 1, 10);
 
     jl_typector_type =
         jl_new_datatype(jl_symbol("TypeConstructor"),
@@ -3628,6 +3638,7 @@ void jl_init_types(void)
     unused_sym = jl_symbol("#unused#");
     slot_sym = jl_symbol("slot");
     static_parameter_sym = jl_symbol("static_parameter");
+    compiler_temp_sym = jl_symbol("#temp#");
 }
 
 #ifdef __cplusplus
diff --git a/src/julia.h b/src/julia.h
index 49e30d140123c..a590d5b0677d7 100644
--- a/src/julia.h
+++ b/src/julia.h
@@ -175,7 +175,11 @@ typedef struct _jl_lambda_info_t {
     // a function pointer.
     // this is the stuff that's shared among different instantiations
     // (different environments) of a closure.
-    jl_value_t *ast;
+    jl_array_t *code;  // compressed uint8 array, or Any array of statements
+    jl_array_t *slotnames; // names of local variables
+    jl_value_t *slottypes;
+    jl_array_t *slotflags;  // local var bit flags
+    jl_value_t *gensymtypes;
     jl_value_t *rettype;
     // sparams is a vector of values indexed by symbols
     jl_svec_t *sparam_syms;
@@ -192,14 +196,14 @@ typedef struct _jl_lambda_info_t {
     struct _jl_lambda_info_t *def;  // original this is specialized from
     jl_sym_t *file;
     int32_t line;
-    int32_t nslots;
-    int32_t ngensym;
+    int32_t nargs;
     int8_t inferred;
     int8_t pure;
+    int8_t isva;
     int8_t inInference; // flags to tell if inference is running on this function
-    uint8_t called;  // bit flags: whether each of the first 8 arguments is called
 
     // hidden fields:
+    uint8_t called;  // bit flags: whether each of the first 8 arguments is called
     uint8_t jlcall_api : 1;     // the c-abi for fptr; 0 = jl_fptr_t, 1 = jl_fptr_sparam_t
     uint8_t inCompile : 1;
     // if there are intrinsic calls, sparams are probably required to compile successfully,
@@ -487,7 +491,7 @@ extern jl_sym_t *null_sym;    extern jl_sym_t *body_sym;
 extern jl_sym_t *method_sym;  extern jl_sym_t *slot_sym;
 extern jl_sym_t *enter_sym;   extern jl_sym_t *leave_sym;
 extern jl_sym_t *exc_sym;     extern jl_sym_t *new_sym;
-extern jl_sym_t *static_typeof_sym;
+extern jl_sym_t *static_typeof_sym; extern jl_sym_t *compiler_temp_sym;
 extern jl_sym_t *const_sym;   extern jl_sym_t *thunk_sym;
 extern jl_sym_t *anonymous_sym;  extern jl_sym_t *underscore_sym;
 extern jl_sym_t *abstracttype_sym; extern jl_sym_t *bitstype_sym;
@@ -644,6 +648,19 @@ STATIC_INLINE jl_value_t *jl_cellset(void *a, size_t i, void *x)
     return (jl_value_t*)x;
 }
 
+STATIC_INLINE uint8_t jl_array_uint8_ref(void *a, size_t i)
+{
+    assert(i < jl_array_len(a));
+    assert(jl_typeis(a, jl_array_uint8_type));
+    return ((uint8_t*)(jl_array_data(a)))[i];
+}
+STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x)
+{
+    assert(i < jl_array_len(a));
+    assert(jl_typeis(a, jl_array_uint8_type));
+    ((uint8_t*)(jl_array_data(a)))[i] = x;
+}
+
 #define jl_exprarg(e,n) (((jl_value_t**)jl_array_data(((jl_expr_t*)(e))->args))[n])
 #define jl_exprargset(e, n, v) jl_cellset(((jl_expr_t*)(e))->args, n, v)
 #define jl_expr_nargs(e) jl_array_len(((jl_expr_t*)(e))->args)
@@ -1228,28 +1245,27 @@ JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m);
 // AST access
 JL_DLLEXPORT int jl_is_rest_arg(jl_value_t *ex);
 
-JL_DLLEXPORT jl_value_t *jl_prepare_ast(jl_lambda_info_t *li);
 JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr);
 
-JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast);
-JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data);
+JL_DLLEXPORT jl_array_t *jl_compress_ast(jl_lambda_info_t *li, jl_array_t *ast);
+JL_DLLEXPORT jl_array_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_array_t *data);
 
 JL_DLLEXPORT int jl_is_operator(char *sym);
 JL_DLLEXPORT int jl_operator_precedence(char *sym);
 
-STATIC_INLINE int jl_vinfo_assigned(jl_array_t *vi)
+STATIC_INLINE int jl_vinfo_assigned(uint8_t vi)
 {
-    return (jl_unbox_long(jl_cellref(vi,2))&2)!=0;
+    return (vi&2)!=0;
 }
 
-STATIC_INLINE int jl_vinfo_sa(jl_array_t *vi)
+STATIC_INLINE int jl_vinfo_sa(uint8_t vi)
 {
-    return (jl_unbox_long(jl_cellref(vi,2))&16)!=0;
+    return (vi&16)!=0;
 }
 
-STATIC_INLINE int jl_vinfo_usedundef(jl_array_t *vi)
+STATIC_INLINE int jl_vinfo_usedundef(uint8_t vi)
 {
-    return (jl_unbox_long(jl_cellref(vi,2))&32)!=0;
+    return (vi&32)!=0;
 }
 
 // calling into julia ---------------------------------------------------------
diff --git a/src/julia_internal.h b/src/julia_internal.h
index f9a3dda8a04a9..6ea7ae0f0a581 100644
--- a/src/julia_internal.h
+++ b/src/julia_internal.h
@@ -194,19 +194,17 @@ jl_lambda_info_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *t
                                            int cache, int inexact);
 jl_lambda_info_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, size_t nargs, int cache);
 jl_value_t *jl_gf_invoke(jl_tupletype_t *types, jl_value_t **args, size_t nargs);
-jl_sym_t *jl_decl_var(jl_value_t *ex);
 
 jl_array_t *jl_lam_args(jl_expr_t *l);
 jl_array_t *jl_lam_vinfo(jl_expr_t *l);
 jl_array_t *jl_lam_capt(jl_expr_t *l);
 jl_value_t *jl_lam_gensyms(jl_expr_t *l);
 jl_array_t *jl_lam_staticparams(jl_expr_t *l);
-jl_sym_t *jl_lam_argname(jl_lambda_info_t *li, int i);
 int jl_lam_vars_captured(jl_expr_t *ast);
 jl_expr_t *jl_lam_body(jl_expr_t *l);
 jl_value_t *jl_first_argument_datatype(jl_value_t *argtypes);
 jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam);
-int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m);
+int jl_has_intrinsics(jl_lambda_info_t *li, jl_value_t *v, jl_module_t *m);
 
 jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i);
 void jl_compute_field_offsets(jl_datatype_t *st);
diff --git a/src/toplevel.c b/src/toplevel.c
index 44a010370fc5b..9f66e6adeacd6 100644
--- a/src/toplevel.c
+++ b/src/toplevel.c
@@ -240,8 +240,10 @@ JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m)
     return jl_top_module;
 }
 
-int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m)
+int jl_has_intrinsics(jl_lambda_info_t *li, jl_value_t *v, jl_module_t *m)
 {
+    if (!jl_is_expr(v)) return 0;
+    jl_expr_t *e = (jl_expr_t*)v;
     if (jl_array_len(e->args) == 0)
         return 0;
     if (e->head == static_typeof_sym)
@@ -261,7 +263,7 @@ int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m)
     int i;
     for (i=0; i < jl_array_len(e->args); i++) {
         jl_value_t *a = jl_exprarg(e,i);
-        if (jl_is_expr(a) && jl_has_intrinsics(li, (jl_expr_t*)a, m))
+        if (jl_is_expr(a) && jl_has_intrinsics(li, a, m))
             return 1;
     }
     return 0;
@@ -269,51 +271,49 @@ int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m)
 
 // heuristic for whether a top-level input should be evaluated with
 // the compiler or the interpreter.
-static int jl_eval_with_compiler_p(jl_lambda_info_t *li, jl_expr_t *expr, int compileloops, jl_module_t *m)
+static int jl_eval_with_compiler_p(jl_lambda_info_t *li, jl_array_t *body, int compileloops, jl_module_t *m)
 {
-    assert(jl_is_expr(expr));
-    if (expr->head==body_sym && compileloops) {
-        jl_array_t *body = expr->args;
-        size_t i, maxlabl=0;
-        // compile if there are backwards branches
-        for(i=0; i < jl_array_len(body); i++) {
-            jl_value_t *stmt = jl_cellref(body,i);
-            if (jl_is_labelnode(stmt)) {
-                int l = jl_labelnode_label(stmt);
-                if (l > maxlabl) maxlabl = l;
-            }
+    size_t i, maxlabl=0;
+    // compile if there are backwards branches
+    for(i=0; i < jl_array_len(body); i++) {
+        jl_value_t *stmt = jl_cellref(body,i);
+        if (jl_is_labelnode(stmt)) {
+            int l = jl_labelnode_label(stmt);
+            if (l > maxlabl) maxlabl = l;
+        }
+    }
+    size_t sz = (maxlabl+1+7)/8;
+    char *labls = (char*)alloca(sz); memset(labls,0,sz);
+    for(i=0; i < jl_array_len(body); i++) {
+        jl_value_t *stmt = jl_cellref(body,i);
+        if (jl_is_labelnode(stmt)) {
+            int l = jl_labelnode_label(stmt);
+            labls[l/8] |= (1<<(l&7));
         }
-        size_t sz = (maxlabl+1+7)/8;
-        char *labls = (char*)alloca(sz); memset(labls,0,sz);
-        for(i=0; i < jl_array_len(body); i++) {
-            jl_value_t *stmt = jl_cellref(body,i);
-            if (jl_is_labelnode(stmt)) {
-                int l = jl_labelnode_label(stmt);
-                labls[l/8] |= (1<<(l&7));
+        else if (compileloops && jl_is_gotonode(stmt)) {
+            int l = jl_gotonode_label(stmt);
+            if (labls[l/8]&(1<<(l&7))) {
+                return 1;
             }
-            else if (compileloops && jl_is_gotonode(stmt)) {
-                int l = jl_gotonode_label(stmt);
+        }
+        else if (jl_is_expr(stmt)) {
+            if (compileloops && ((jl_expr_t*)stmt)->head==goto_ifnot_sym) {
+                int l = jl_unbox_long(jl_exprarg(stmt,1));
                 if (labls[l/8]&(1<<(l&7))) {
                     return 1;
                 }
             }
-            else if (jl_is_expr(stmt)) {
-                if (compileloops && ((jl_expr_t*)stmt)->head==goto_ifnot_sym) {
-                    int l = jl_unbox_long(jl_exprarg(stmt,1));
-                    if (labls[l/8]&(1<<(l&7))) {
-                        return 1;
-                    }
-                }
-                // to compile code that uses exceptions
-                /*
-                if (((jl_expr_t*)stmt)->head == enter_sym) {
-                    return 1;
-                }
-                */
-            }
         }
+        if (jl_has_intrinsics(li, stmt, m)) return 1;
     }
-    if (jl_has_intrinsics(li, expr, m)) return 1;
+    return 0;
+}
+
+static int jl_eval_expr_with_compiler_p(jl_value_t *e, int compileloops, jl_module_t *m)
+{
+    if (jl_is_expr(e) && ((jl_expr_t*)e)->head == body_sym)
+        return jl_eval_with_compiler_p(NULL, ((jl_expr_t*)e)->args, compileloops, m);
+    if (jl_has_intrinsics(NULL, e, m)) return 1;
     return 0;
 }
 
@@ -519,14 +519,11 @@ jl_value_t *jl_toplevel_eval_flex(jl_value_t *e, int fast)
     if (head == thunk_sym) {
         thk = (jl_lambda_info_t*)jl_exprarg(ex,0);
         assert(jl_is_lambda_info(thk));
-        if (!jl_is_expr(thk->ast)) {
-            thk->ast = jl_uncompress_ast(thk, thk->ast);
-            jl_gc_wb(thk, thk->ast);
-        }
-        ewc = jl_eval_with_compiler_p(thk, jl_lam_body((jl_expr_t*)thk->ast), fast, jl_current_module);
+        assert(jl_typeis(thk->code, jl_array_any_type));
+        ewc = jl_eval_with_compiler_p(thk, thk->code, fast, jl_current_module);
     }
     else {
-        if (head && jl_eval_with_compiler_p(NULL, (jl_expr_t*)ex, fast, jl_current_module)) {
+        if (head && jl_eval_expr_with_compiler_p((jl_value_t*)ex, fast, jl_current_module)) {
             thk = jl_wrap_expr((jl_value_t*)ex);
             ewc = 1;
         }
@@ -616,25 +613,19 @@ void print_func_loc(JL_STREAM *s, jl_lambda_info_t *li);
 
 void jl_check_static_parameter_conflicts(jl_lambda_info_t *li, jl_svec_t *t, jl_sym_t *fname)
 {
-    jl_array_t *vinfo;
-    size_t nvars;
-
-    if (li->ast && jl_is_expr(li->ast)) {
-        vinfo = jl_lam_vinfo((jl_expr_t*)li->ast);
-        nvars = jl_array_len(vinfo);
-        for(size_t i=0; i < jl_svec_len(t); i++) {
-            for(size_t j=0; j < nvars; j++) {
-                jl_value_t *tv = jl_svecref(t,i);
-                if (jl_is_typevar(tv)) {
-                    if ((jl_sym_t*)jl_cellref((jl_array_t*)jl_cellref(vinfo,j),0) ==
-                        ((jl_tvar_t*)tv)->name) {
-                        jl_printf(JL_STDERR,
-                                  "WARNING: local variable %s conflicts with a static parameter in %s",
-                                  jl_symbol_name(((jl_tvar_t*)tv)->name),
-                                  jl_symbol_name(fname));
-                        print_func_loc(JL_STDERR, li);
-                        jl_printf(JL_STDERR, ".\n");
-                    }
+    size_t nvars = jl_array_len(li->slotnames);
+
+    for(size_t i=0; i < jl_svec_len(t); i++) {
+        for(size_t j=0; j < nvars; j++) {
+            jl_value_t *tv = jl_svecref(t,i);
+            if (jl_is_typevar(tv)) {
+                if ((jl_sym_t*)jl_cellref(li->slotnames, j) == ((jl_tvar_t*)tv)->name) {
+                    jl_printf(JL_STDERR,
+                              "WARNING: local variable %s conflicts with a static parameter in %s",
+                              jl_symbol_name(((jl_tvar_t*)tv)->name),
+                              jl_symbol_name(fname));
+                    print_func_loc(JL_STDERR, li);
+                    jl_printf(JL_STDERR, ".\n");
                 }
             }
         }
@@ -726,7 +717,7 @@ static jl_lambda_info_t *expr_to_lambda(jl_expr_t *f)
     }
     // wrap in a LambdaInfo
     jl_lambda_info_t *li = jl_new_lambda_info((jl_value_t*)f, tvar_syms, jl_emptysvec, jl_current_module);
-    jl_preresolve_globals(li->ast, li);
+    jl_preresolve_globals((jl_value_t*)li, li);
     JL_GC_POP();
     return li;
 }
@@ -771,7 +762,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_lambda_info_t *f, jl_valu
         jl_value_t *elt = jl_tparam(argtypes,i);
         if (!jl_is_type(elt) && !jl_is_typevar(elt)) {
             jl_exceptionf(jl_argumenterror_type, "invalid type for argument %s in method definition for %s at %s:%d",
-                          jl_symbol_name(jl_lam_argname(f,i)),
+                          jl_symbol_name((jl_sym_t*)jl_cellref(f->slotnames,i)),
                           jl_symbol_name(name), jl_symbol_name(f->file),
                           f->line);
         }
@@ -792,9 +783,9 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_lambda_info_t *f, jl_valu
     }
 
     jl_add_method_to_table(mt, argtypes, f, tvars, isstaged == jl_true);
-    if (jl_boot_file_loaded && f->ast && jl_is_expr(f->ast)) {
-        f->ast = jl_compress_ast(f, f->ast);
-        jl_gc_wb(f, f->ast);
+    if (jl_boot_file_loaded && f->code && jl_typeis(f->code, jl_array_any_type)) {
+        f->code = jl_compress_ast(f, f->code);
+        jl_gc_wb(f, f->code);
     }
     JL_GC_POP();
 }
diff --git a/test/core.jl b/test/core.jl
index dca1c6d3b2aa6..34049084b52fd 100644
--- a/test/core.jl
+++ b/test/core.jl
@@ -3189,7 +3189,7 @@ typealias PossiblyInvalidUnion{T} Union{T,Int}
 @test split(string(gensym("abc")),'#')[3] == "abc"
 
 # meta nodes for optional positional arguments
-@test Base.uncompressed_ast(expand(:(@inline f(p::Int=2) = 3)).args[2].args[3]).args[3].args[1].args[1] === :inline
+@test Base.uncompressed_ast(expand(:(@inline f(p::Int=2) = 3)).args[2].args[3])[1].args[1] === :inline
 
 # issue #13007
 call13007{T,N}(::Type{Array{T,N}}) = 0
diff --git a/test/inference.jl b/test/inference.jl
index c3afe6ffd9223..71f1641187755 100644
--- a/test/inference.jl
+++ b/test/inference.jl
@@ -185,9 +185,7 @@ end
 end
 let
     ast12474 = code_typed(f12474, Tuple{Float64})
-    for (_, vartype) in ast12474[1].args[2][1]
-        @test isleaftype(vartype)
-    end
+    @test all(isleaftype, ast12474[1].slottypes)
 end
 
 
@@ -200,7 +198,7 @@ end
 @eval f15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); (a.x, a.y, getfield(a,1), getfield(a, 2)))
 @test isempty(filter(x -> isa(x,Expr) && x.head === :(=) &&
                           isa(x.args[2], Expr) && x.args[2].head === :new,
-                     code_typed(f15259, (Any,Int))[1].args[3].args))
+                     code_typed(f15259, (Any,Int))[1].code))
 @test f15259(1,2) == (1,2,1,2)
 # check that error cases are still correct
 @eval g15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); a.z)
diff --git a/test/inline.jl b/test/inline.jl
index 917dc4e045171..c66ada585dadb 100644
--- a/test/inline.jl
+++ b/test/inline.jl
@@ -20,9 +20,8 @@ Helper to test that every slot is in range after inlining.
 """
 function test_inlined_symbols(func, argtypes)
     linfo = code_typed(func, argtypes)[1]
-    locals = linfo.args[2][1]
-    nl = length(locals)
-    ast = linfo.args[3]
+    nl = length(linfo.slottypes)
+    ast = Expr(:body); ast.args = Base.uncompressed_ast(linfo)
     walk(ast) do e
         if isa(e, Slot)
             @test 1 <= e.id <= nl
diff --git a/test/meta.jl b/test/meta.jl
index e1e076aec6cac..46f47eb7832ac 100644
--- a/test/meta.jl
+++ b/test/meta.jl
@@ -61,7 +61,7 @@ asts = code_lowered(dummy, Tuple{})
 ast = asts[1]
 
 body = Expr(:block)
-body.args = ast.args[3].args
+body.args = Base.uncompressed_ast(ast)
 
 @test popmeta!(body, :test) == (true, [42])
 @test popmeta!(body, :nonexistent) == (false, [])