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
OpDefproto'sArgDefmembers contain a type string, which must be dereferenced to anAttrDefby name to determine its type. ThisAttrDefproto message in turn contains a type string which may need to be parsed as "list(type)". OtherAttrDefmessages are not types, but instead argument-like modifiers. In contrast, the generator modelArgSpeccontains a resolvedArgTypewhich provides a booleanis_list()method directly, and the modelOpSpecprovides 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,
RenderersuseViewsand otherRenderers. However,Renderersdo 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::InitMainand parse any flags - Initialize config objects (e.g.
PathConfig,LangConfigfrom flags) - Initialize a new
LangGeneratorfrom these config objects - Call this generator to create/write
SourceCodeto a file
In class LangGenerator in lang_generator.cc:
- Initialize a new
Controllerfrom the config objects - Call this controller to build the Op models (
OpSpec) - Initialize a new language-specific
Viewfor each model object - Create a blank
SourceCoderendering target (for each output file) - Initialize a new
LangFileRendererfrom this target source code, the modelViewobjects, and config objects - Call this renderer to generate the target
SourceCode
The dependencies are as follows:
lang::Generatordepends onController,Model,lang::Renderers,lang::Viewslang::Rendererdepends onlang::View(andlang::Rendererpeers)lang::Viewdepends on the model (e.g.OpSpec) (andlang::Viewpeers)