Code Generation

Types

ComputableDAGs.FunctionCallType
FunctionCall{VAL_T<:Tuple,FUNC_T<:Union{Function,Expr}}

Representation of a function call. Contains the function to call (or an expression of a value to assign), value arguments of type VAL_T, argument symbols, the return symbol(s) and type(s) and the device to execute on.

To support vectorization, i.e., calling the same function on multiple inputs (SIMD), the value arguments, arguments, and return symbols are each vectors of the actual inputs. In the non-vectorized case, these Vectors simply always have length 1. For this common case, a special constructor exists which automatically wraps each of these arguments in a Vector.

Type Arguments

  • VAL_T<:Tuple: A tuple of all the value arguments that are passed to the function when it's called.
  • FUNC_T<:Union{Function, Expr}: The type of the function. Function is the default, but in some cases, an Expr of a value can be necessary to assign to the return symbol. In this case, no arguments are allowed.

Fields

  • func::FUNC_T: The function to be called, or an expression containing a value to assign to the return_symbol.
  • value_arguments::Vector{VAL_T}: The value arguments for the function call. These are passed first to the function, in the order given here. The Vector contains the tuple of value arguments for each vectorization member.
  • arguments::Vector{Vector{Symbol}}: The first vector represents the vectorization, the second layer represents the symbols that will be passed as arguments to the function call.
  • return_symbols::Vector{Vector{Symbol}}: As with the arguments, the first vector level represents the vectorization, the second represents the symbols that the results of the function call are assigned to. For most function calls, there is only one return symbol. When using closures when generating a function body for a Tape, the option to have multiple return symbols is necessary.
  • return_types::Vector{<:Type}: The types of the function call with the arguments provided. This field only contains one level of Vector, because it is required that a FunctionCall is type stable, and therefore, the types of the return symbols have to be equal for all members of a vectorization. The return type is initially set to Nothing and later inferred and assigned by infer_types!.
  • device::AbstractDevice: The device that this function call is scheduled on.
source
ComputableDAGs.TapeType
Tape{INPUT}

Lowered representation of a computation, generated from a DAG through gen_tape.

  • INPUT the input type of the problem instance, see also the interface function input_type

Fields

  • input_assign_code::Vector{FunctionCall}: The FunctionCalls representing the input assignments, mapping part of the input of the computation to each DAG entry node. These functions are generated using the interface function input_expr.
  • schedule::Vector{FunctionCall}: The FunctionCalls representing the function body of the computation. There is one function call for each node in the DAG.
  • output_symbol::Symbol: The symbol of the final calculated value, which is returned.
  • instance::Any: The instance that this tape is generated for.
  • machine::Machine: The Machine that this tape is generated for.
source

Function Generation

Implementations for generation of a callable function. A function generated this way cannot immediately be called. One Julia World Age has to pass before this is possible, which happens when the global Julia scope advances. If the DAG and therefore the generated function becomes too large, use the tape machine instead, since compiling large functions becomes infeasible.

ComputableDAGs.get_compute_functionMethod
get_compute_function(
    graph::DAG,
    instance,
    machine::Machine,
    context_module::Module
)

Return a function of signature compute_<id>(input::input_type(instance)), which will return the result of the DAG computation on the given input. The final argument context_module should always be @__MODULE__ to be able to use functions defined in the caller's environment. For this to work, you need

using RuntimeGeneratedFunctions
RuntimeGeneratedFunctions.init(@__MODULE__)

in your top level.

Keyword Arguments

closures_size (default=0 (off)): The size of closures to use in the main generated code. This specifies the size of code blocks across which the compiler cannot optimize. For sufficiently large functions, a larger value means longer compile times but potentially faster execution time. Note that the actually used closure size might be different than the one passed here, since the function automatically chooses a size that is close to a n-th root of the total number of loc, based off the given size. concrete_input_type (default=input_type(instance)): A type that will be used as the expected input type of the generated function. If omitted, the input_type of the problem instance is used. Note that the input_type of the instance will still be used as the annotated type in the generated function header.

source

Tape Machine

ComputableDAGs._closure_fcMethod
_closure_fc(
    code_block::AbstractVector{FunctionCall},
    types::Dict{Symbol,Type},
    machine::Machine,
    undefined_argument_symbols::Set{Symbol},
    context_module::Module,
)

From the given function calls, make and return 2 function calls representing all of them together. 2 function calls are necessary, one for setting up the anonymous function and the second for calling it. The undefinedargumentsymbols is the set of all Symbols that need to be returned if available inside the code_block. They get updated inside this function.

source
ComputableDAGs.gen_function_bodyMethod
gen_function_body(tape::Tape, context_module::Module; closures_size)

Generate the function body from the given Tape.

Keyword Arguments

closures_size: The size of closures to generate (in lines of code). Closures introduce function barriers in the function body, preventing some optimizations by the compiler and therefore greatly reducing compile time. A value of 0 will disable the use of closures entirely. concrete_input_type: A type that will be used as the expected input type of the generated function. If omitted, the input_type of the problem instance is used.

source
ComputableDAGs.gen_input_assignment_codeMethod
gen_input_assignment_code(
    input_symbols::Dict{String, Vector{Symbol}},
    instance::Any,
    machine::Machine,
    input_type::Type,
    context_module::Module
)

Return a Vector{Expr} doing the input assignments from the given problem_input onto the input_symbols.

source
ComputableDAGs.gen_tapeFunction
gen_tape(
    graph::DAG,
    instance::Any,
    machine::Machine,
    context_module::Module,
    scheduler::AbstractScheduler = GreedyScheduler()
)

Generate the code for a given graph. The return value is a Tape.

source