From 349baa1c47968a52d07df13ef982f02dfc42070d Mon Sep 17 00:00:00 2001 From: serenity4 Date: Sat, 26 Jun 2021 14:55:54 +0200 Subject: [PATCH 1/3] Add AddFPtrMethod pass --- src/generator/context.jl | 4 ++++ src/generator/passes.jl | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/generator/context.jl b/src/generator/context.jl index 90d51fd7..f90ea9f6 100644 --- a/src/generator/context.jl +++ b/src/generator/context.jl @@ -116,6 +116,10 @@ function create_context(headers::Vector, args::Vector=String[], options::Dict=Di push!(ctx.passes, TweakMutability()) end + if get(general_options, "add_fptr_methods", false) + push!(ctx.passes, AddFPtrMethods()) + end + # support old behavior api_file = get(general_options, "output_api_file_path", "") common_file = get(general_options, "output_common_file_path", "") diff --git a/src/generator/passes.jl b/src/generator/passes.jl index 0009ea99..a24ff7e8 100644 --- a/src/generator/passes.jl +++ b/src/generator/passes.jl @@ -756,6 +756,51 @@ function (x::TweakMutability)(dag::ExprDAG, options::Dict) return dag end +""" + AddFPtrMethods <: AbstractPass +This pass adds a method definition for each function prototype method which `ccall`s into a library. + +The generated method allows the use of a function pointer to `ccall` into directly, instead +of relying on a library to give the pointer via a symbol look up. This is useful for libraries that +use runtime loaders to dynamically resolve function pointers for API calls. +""" +struct AddFPtrMethods <: AbstractPass end + +function (::AddFPtrMethods)(dag::ExprDAG, options::Dict) + codegen_options = get(options, "codegen", Dict()) + use_ccall_macro = get(codegen_options, "use_ccall_macro", false) + for node in dag.nodes + node.type isa FunctionProto || continue + for _ex in copy(node.exprs) + ex = deepcopy(_ex) + Base.is_expr(ex, :function) || continue + if Base.is_expr(ex.args[1], :where) + call = ex.args[1].args[1] + else + call = ex.args[1] + end + push!(call.args, :fptr) + body_idx = findfirst(x -> Base.is_expr(x, :block), ex.args) + body = ex.args[body_idx] + idx = if use_ccall_macro + findfirst(x -> Base.is_expr(x, :macrocall) && x.args[1] == Symbol("@ccall"), body.args) + else + findfirst(x -> Base.is_expr(x, :call) && x.args[1] == :ccall, body.args) + end + !isnothing(idx) || continue + stmt = body.args[idx] + if use_ccall_macro + assert_idx = findfirst(x -> Base.is_expr(x, :(::)), stmt.args) + call_idx = findfirst(x -> Base.is_expr(x, :call), stmt.args[assert_idx].args) + stmt.args[assert_idx].args[call_idx].args[1] = Expr(:$, :fptr) + else + stmt.args[2] = :fptr + end + push!(node.exprs, ex) + end + end +end + const DEFAULT_AUDIT_FUNCTIONS = [ audit_library_name, sanity_check, From fc816c74c463cb13e7596ce08863f2318690627d Mon Sep 17 00:00:00 2001 From: serenity4 Date: Sun, 27 Jun 2021 10:47:45 +0200 Subject: [PATCH 2/3] Move pass just after codegen --- src/generator/context.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/generator/context.jl b/src/generator/context.jl index f90ea9f6..5da69549 100644 --- a/src/generator/context.jl +++ b/src/generator/context.jl @@ -112,13 +112,12 @@ function create_context(headers::Vector, args::Vector=String[], options::Dict=Di push!(ctx.passes, Codegen()) push!(ctx.passes, CodegenMacro()) # push!(ctx.passes, CodegenPostprocessing()) - if get(general_options, "auto_mutability", false) - push!(ctx.passes, TweakMutability()) - end - if get(general_options, "add_fptr_methods", false) push!(ctx.passes, AddFPtrMethods()) end + if get(general_options, "auto_mutability", false) + push!(ctx.passes, TweakMutability()) + end # support old behavior api_file = get(general_options, "output_api_file_path", "") From 296ee53409f1510d55fb8416d8b0b432fcf7efbc Mon Sep 17 00:00:00 2001 From: serenity4 Date: Sun, 27 Jun 2021 10:49:06 +0200 Subject: [PATCH 3/3] Simplify code and remove unnecessary checks --- src/generator/passes.jl | 43 ++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/generator/passes.jl b/src/generator/passes.jl index a24ff7e8..1409c9b0 100644 --- a/src/generator/passes.jl +++ b/src/generator/passes.jl @@ -771,33 +771,24 @@ function (::AddFPtrMethods)(dag::ExprDAG, options::Dict) use_ccall_macro = get(codegen_options, "use_ccall_macro", false) for node in dag.nodes node.type isa FunctionProto || continue - for _ex in copy(node.exprs) - ex = deepcopy(_ex) - Base.is_expr(ex, :function) || continue - if Base.is_expr(ex.args[1], :where) - call = ex.args[1].args[1] - else - call = ex.args[1] - end - push!(call.args, :fptr) - body_idx = findfirst(x -> Base.is_expr(x, :block), ex.args) - body = ex.args[body_idx] - idx = if use_ccall_macro - findfirst(x -> Base.is_expr(x, :macrocall) && x.args[1] == Symbol("@ccall"), body.args) - else - findfirst(x -> Base.is_expr(x, :call) && x.args[1] == :ccall, body.args) - end - !isnothing(idx) || continue - stmt = body.args[idx] - if use_ccall_macro - assert_idx = findfirst(x -> Base.is_expr(x, :(::)), stmt.args) - call_idx = findfirst(x -> Base.is_expr(x, :call), stmt.args[assert_idx].args) - stmt.args[assert_idx].args[call_idx].args[1] = Expr(:$, :fptr) - else - stmt.args[2] = :fptr - end - push!(node.exprs, ex) + ex = copy(first(node.exprs)) + call = ex.args[1] + push!(call.args, :fptr) + body = ex.args[findfirst(x -> Base.is_expr(x, :block), ex.args)] + stmt_idx = if use_ccall_macro + findfirst(x -> Base.is_expr(x, :macrocall) && x.args[1] == Symbol("@ccall"), body.args) + else + findfirst(x -> Base.is_expr(x, :call) && x.args[1] == :ccall, body.args) + end + stmt = body.args[stmt_idx] + if use_ccall_macro + typeassert = stmt.args[findfirst(x -> Base.is_expr(x, :(::)), stmt.args)] + call = typeassert.args[findfirst(x -> Base.is_expr(x, :call), typeassert.args)] + call.args[1] = Expr(:$, :fptr) + else + stmt.args[2] = :fptr end + push!(node.exprs, ex) end end