Back to Tensorflow

TensorFlow Op CodeGen Machinery (Experimental)

tensorflow/c/experimental/ops/gen/README.md

2.21.05.7 KB
Original Source

TensorFlow Op CodeGen Machinery (Experimental)

Usage

usage: generate_cpp  [flags]  OpName1 [OpName2 ...]
Flags:
    --help=false                        bool    Print this help message.
    --category=""                       string  Category for generated ops (e.g. 'math', 'array').
    --namespace=""                      string  Compact C++ namespace, default is 'tensorflow::ops'.
    --output_dir=""                     string  Directory into which output files will be generated.
    --source_dir=""                     string  The tensorflow root directory, e.g. 'tensorflow/' for in-source include paths. Any path underneath the tensorflow root is also accepted.
    --api_dirs=""                       string  Comma-separated list of directories containing API definitions.

Design

Generator Framework

The generator framework is a loose Model/View/Controller arrangement:

The Model classes live in the model/ directory. They are representations of the OpDef and ApiDef protos, normalized and resolved.

For example, an OpDef proto's ArgDef members contain a type string, which must be dereferenced to an AttrDef by name to determine its type. This AttrDef proto message in turn contains a type string which may need to be parsed as "list(type)". Other AttrDef messages are not types, but instead argument-like modifiers. In contrast, the generator model ArgSpec contains a resolved ArgType which provides a boolean is_list() method directly, and the model OpSpec provides a list of only the argument-like attributes. In addition to convenience, this should aid consistency between generated code in each target language.

The Controller is in the common/ directory. It is the workhorse used by the language generators; it digests the Op registry and API definitions to build the model and provides utilities for the language generators.

The View and rendering classes map the language-independent Model classes (OpSpec, ArgSpec, AttrSpec, etc.) to language-specific SourceCode. The framework does not impose any design on the language-specific generators, but provides some utilities, and the C++ generator is a complete example.

C++ Generator

The CppGenerator class is the interface to the cpp/ language directory. Given a config, it can generate source code for a .cc or .h file as a string or write it to a target file.

The CppFileRenderer is the main renderer used by the generator; it renders an entire file. The CppConfig defines if it is operating in header or source mode.

"Views" are stateless and intended to be low-level building blocks: a direct language-specific representation of the model classes. For example, an ArgView is initialized from an ArgSpec (which was created initially from an ArgDef proto message). Where they may have some similar methods between the model and view, the view methods are language-specific.

For instance, the C++ generator's ArgView::VariableName() method is an language-formatted name usable as a variable representing the model ArgSpec object. In contrast, the ArgSpec::name() method in the model refers to the canonical name of the object in the proto.

Where views are a representation of the input model, in the C++ generator, "renderers" then use these views to build the output SourceCode; Renderers understand the language at the statement/directive level and target a functional section of the output, such as a block comment or an entire method or file.

Other differences between views and renderers:

  • Renderers are stateful, modifying a referenced SourceCode. Views are stateless and their public methods are all const, returning strings.
  • Renderers are context-dependent, e.g. a method signature will include default values when in "declaration" mode but not "definition" mode. A view of some argument object simply knows its default value and does not care the context.
  • In terms of dependencies, Renderers use Views and other Renderers. However, Renderers do not reference the model directly (e.g. OpSpec). This is because if a renderer needs to reference part of the model, it should get a language specific representation.

Extending to Additional Languages

The design for the C++ generator should apply to other languages, and the underlying generator framework (the model and controller) try to be agnostic. In fact, some elements of the C++ design could be formalized (such as the rendering/view framework) or re-used (e.g. cpp:Renderer could likely be shared with C and Java as a common C-style language renderer base class).

Abstracted and condensed from the C++ generator, the overall control flow could be described as follows:

From main() in generate_lang_main.cc:

  • Call tensorflow::port::InitMain and parse any flags
  • Initialize config objects (e.g. PathConfig, LangConfig from flags)
  • Initialize a new LangGenerator from these config objects
  • Call this generator to create/write SourceCode to a file

In class LangGenerator in lang_generator.cc:

  • Initialize a new Controller from the config objects
  • Call this controller to build the Op models (OpSpec)
  • Initialize a new language-specific View for each model object
  • Create a blank SourceCode rendering target (for each output file)
  • Initialize a new LangFileRenderer from this target source code, the model View objects, and config objects
  • Call this renderer to generate the target SourceCode

The dependencies are as follows:

  • lang::Generator depends on Controller, Model, lang::Renderers, lang::Views
  • lang::Renderer depends on lang::View (and lang::Renderer peers)
  • lang::View depends on the model (e.g. OpSpec) (and lang::View peers)