@@ -5,8 +5,14 @@ import Base: +, -, convert, isless
5
5
using Core: CodeInfo, SSAValue, SlotNumber, TypeMapEntry, SimpleVector, LineInfoNode, GotoNode, Slot,
6
6
GeneratedFunctionStub, MethodInstance, NewvarNode, TypeName
7
7
8
+ using UUIDs
9
+
8
10
export @enter , @make_stack , @interpret , Compiled, JuliaStackFrame
9
11
12
+ module CompiledCalls
13
+ # This module is for handling intrinsics that must be compiled (llvmcall)
14
+ end
15
+
10
16
"""
11
17
`Compiled` is a trait indicating that any `:call` expressions should be evaluated
12
18
using Julia's normal compiled-code evaluation. The alternative is to pass `stack=JuliaStackFrame[]`,
@@ -50,7 +56,7 @@ Important fields:
50
56
struct JuliaFrameCode
51
57
scope:: Union{Method,Module}
52
58
code:: CodeInfo
53
- methodtables:: Vector{TypeMapEntry} # line-by-line method tables for generic-function :call Exprs
59
+ methodtables:: Vector{Union{Compiled, TypeMapEntry} } # line-by-line method tables for generic-function :call Exprs
54
60
used:: BitSet
55
61
wrapper:: Bool
56
62
generator:: Bool
@@ -63,10 +69,14 @@ function JuliaFrameCode(frame::JuliaFrameCode; wrapper = frame.wrapper, generato
63
69
wrapper, generator, fullpath)
64
70
end
65
71
66
- function JuliaFrameCode (scope, code:: CodeInfo ; wrapper= false , generator= false , fullpath= true )
67
- code = optimize! (copy_codeinfo (code), moduleof (scope))
72
+ function JuliaFrameCode (scope, code:: CodeInfo ; wrapper= false , generator= false , fullpath= true , optimize= true )
73
+ if optimize
74
+ code, methodtables = optimize! (copy_codeinfo (code), moduleof (scope))
75
+ else
76
+ code = copy_codeinfo (code)
77
+ methodtables = Vector {Union{Compiled,TypeMapEntry}} (undef, length (code. code))
78
+ end
68
79
used = find_used (code)
69
- methodtables = Vector {TypeMapEntry} (undef, length (code. code))
70
80
return JuliaFrameCode (scope, code, methodtables, used, wrapper, generator, fullpath)
71
81
end
72
82
@@ -612,6 +622,29 @@ function renumber_ssa!(stmts::Vector{Any}, ssalookup)
612
622
return stmts
613
623
end
614
624
625
+ # Pre-frame-construction lookup
626
+ function lookup_stmt (stmts, arg)
627
+ if isa (arg, SSAValue)
628
+ arg = stmts[arg. id]
629
+ end
630
+ if isa (arg, QuoteNode)
631
+ arg = arg. value
632
+ end
633
+ return arg
634
+ end
635
+
636
+ function smallest_ref (stmts, arg, idmin)
637
+ if isa (arg, SSAValue)
638
+ idmin = min (idmin, arg. id)
639
+ return smallest_ref (stmts, stmts[arg. id], idmin)
640
+ elseif isa (arg, Expr)
641
+ for a in arg. args
642
+ idmin = smallest_ref (stmts, a, idmin)
643
+ end
644
+ end
645
+ return idmin
646
+ end
647
+
615
648
function lookup_global_refs! (ex:: Expr )
616
649
(ex. head == :isdefined || ex. head == :thunk || ex. head == :toplevel ) && return nothing
617
650
for (i, a) in enumerate (ex. args)
@@ -676,7 +709,48 @@ function optimize!(code::CodeInfo, mod::Module)
676
709
ssalookup = cumsum (ssainc)
677
710
renumber_ssa! (new_code, ssalookup)
678
711
code. ssavaluetypes = length (new_code)
679
- return code
712
+
713
+ # Replace :llvmcall and :foreigncall with compiled variants. See
714
+ # https://github.com/JuliaDebug/JuliaInterpreter.jl/issues/13#issuecomment-464880123
715
+ methodtables = Vector {Union{Compiled,TypeMapEntry}} (undef, length (code. code))
716
+ for (idx, stmt) in enumerate (code. code)
717
+ if isexpr (stmt, :call )
718
+ # Check for :llvmcall
719
+ arg1 = stmt. args[1 ]
720
+ if arg1 == :llvmcall || lookup_stmt (code. code, arg1) == Base. llvmcall
721
+ uuid = uuid4 ()
722
+ ustr = replace (string (uuid), ' -' => ' _' )
723
+ methname = Symbol (" llvmcall_" , ustr)
724
+ nargs = length (stmt. args)- 4
725
+ argnames = [Symbol (" arg" , string (i)) for i = 1 : nargs]
726
+ # Run a mini-interpreter to extract the types
727
+ framecode = JuliaFrameCode (CompiledCalls, code; optimize= false )
728
+ frame = prepare_locals (framecode, [])
729
+ idxstart = idx
730
+ for i = 2 : 4
731
+ idxstart = smallest_ref (code. code, stmt. args[i], idxstart)
732
+ end
733
+ frame. pc[] = JuliaProgramCounter (idxstart)
734
+ while true
735
+ pc = step_expr! (Compiled (), frame)
736
+ convert (Int, pc) == idx && break
737
+ pc === nothing && error (" this should never happen" )
738
+ end
739
+ str, RetType, ArgType = @lookup (frame, stmt. args[2 ]), @lookup (frame, stmt. args[3 ]), @lookup (frame, stmt. args[4 ])
740
+ def = quote
741
+ function $methname ($ (argnames... ))
742
+ return Base. llvmcall ($ str, $ RetType, $ ArgType, $ (argnames... ))
743
+ end
744
+ end
745
+ f = Core. eval (CompiledCalls, def)
746
+ stmt. args[1 ] = QuoteNode (f)
747
+ deleteat! (stmt. args, 2 : 4 )
748
+ methodtables[idx] = Compiled ()
749
+ end
750
+ end
751
+ end
752
+
753
+ return code, methodtables
680
754
end
681
755
682
756
function prepare_locals (framecode, argvals:: Vector{Any} )
0 commit comments