STT-tensorflow/tensorflow/compiler/xla/service/hlo_verifier.cc
Yifei Feng 18f3692716 Branch 167812735 ()
* Internal cleanup

PiperOrigin-RevId: 167636242

* Move the Keras API to tf.keras.

PiperOrigin-RevId: 167638421

* Automated g4 rollback of changelist 167604306

PiperOrigin-RevId: 167639833

* Call HloComputation.Accept instead of HloInstruction.Accept to get all instructions profiled.

RELNOTES: n/a
PiperOrigin-RevId: 167640259

* Add fast math attributes to all generated methods when fast math enabled.

RELNOTES: n/a
PiperOrigin-RevId: 167646637

* Extended ScratchSpace to expose its underlying scratch tensor object.

PiperOrigin-RevId: 167649551

* Change zip(...)[1] to list(zip(...))[1], for python 3 compatibility.

PiperOrigin-RevId: 167654035

* Add scoped timer to log jit compile times.

RELNOTES: n/a
PiperOrigin-RevId: 167656720

* Verify that predictions are in the expected range for ops that use thresholds, e.g. tf.contrib.metrics.streaming_auc.

PiperOrigin-RevId: 167658134

* Internal change.

PiperOrigin-RevId: 167658401

* Fix list formatting

PiperOrigin-RevId: 167660250

* Enable java test.

PiperOrigin-RevId: 167660276

* Add shape functions on debug ops.

PiperOrigin-RevId: 167668811

* Increase session_bundle_test to a medium test.

PiperOrigin-RevId: 167672587

* Include layout of convolution input data in the op_profile.

PiperOrigin-RevId: 167680208

* Fix tf.sparse_add for SparseTensor with _ref typed values.

Example:
st = tf.SparseTensor(
    indices=[[1]], values=tf.Variable([1.0]), dense_shape=[1])
tf.sparse_add(st, st)
PiperOrigin-RevId: 167681121

* Fix conversion to explicit scalar broadcast

The dimensions field of a broadcast HLO op is meant to be populated with the
dimensions that are broadcasted, which in case of a scalar is the empty vector.
Generally, the rank of the operand of a broadcast op should always equal the
size of the dimensions vector.

PiperOrigin-RevId: 167686946

* Add 'unknown shape' shape functions on deprecated linalg ops.

PiperOrigin-RevId: 167719029

* Be more careful in IsInitalized, and log when it is called on an unknown
node_id.

PiperOrigin-RevId: 167722344

* tfdbg: Refactor graph-processing code out of debug_data.py

The basic idea is to separate the code in debug_data.py that handles graph structures into its own module (debug_graphs.py). This tackles an existing TODO item to simplify the code debug_data.DebugDumpDir.

In a later CL, code will be added to debug_graphs.DebugGraph to allow reconstruction of the original GraphDef, i.e., the GraphDef without the Copy* and Debug* nodes inserted by tfdbg. This will be useful for, among other things, the TensorBoard Debugger Plugin.

PiperOrigin-RevId: 167726113

* internal

PiperOrigin-RevId: 167727508

* Update MaxPoolV2Shape to support NCHV_VECT_C.

PiperOrigin-RevId: 167732437

* Deleting tf.contrib.learn.dnn benchmark tests.

PiperOrigin-RevId: 167741308

* Fix off-by-one documentation error.

sequence_lengths is the actual length of the sequence and therefor should not be used as zero-based indexing.
The code is correct but the documentation was misleading.

PiperOrigin-RevId: 167742082

* contrib summaries work in eager-graph mode (with defun)

As a side effect fix issues related to using eager-defined variables in graph
mode.

PiperOrigin-RevId: 167744121

* Fix minor documentation error in ZlibInputStream.

PiperOrigin-RevId: 167745218

* Sets the distributed training related properties of RunConfig based on TF_CONFIG.

PiperOrigin-RevId: 167752997

* Improved documentation about eval ops in EstimatorSpec.

PiperOrigin-RevId: 167753099

* Automated g4 rollback of changelist 156748870

PiperOrigin-RevId: 167753805

* Make cuda_solvers_gpu.cu.cc compile with nvcc8.

PiperOrigin-RevId: 167754383

* Add csv dataset example to get_started/regression.

PiperOrigin-RevId: 167754634

* Switches to OrderedDict to make the dictionary order deterministic so we have less randomness from graph building.

PiperOrigin-RevId: 167755072

* Add int8 version of fused_conv2d_bias_activation operator for the forward phase,
and support side_input and scaling parameters in float and int8 versions.

PiperOrigin-RevId: 167763219

* Make the text summary write no plugin data content

This is actually a safe removal because no logic makes use of the content of text plugin data.

PiperOrigin-RevId: 167763880

* Avoid unnecessary buffer allocations & deallocations

Before this change, when we reached the end of a file, we would
 (1) clear the existing buffer (which at large buffer sizes typically involved
     deallocating it).
 (2) reserve a buffer (which at large buffer sizes is non-trivial)
 (3) realize we had reached EoF, and therefore clear the buffer, deallocating
     it again.

With this change, whenever the buffered reader detects an EoF condition, we
remember it, so that we can short-circuit the above logic.

The above optimization results in a more than 25x performance improvement for
large buffers reading small files.

PiperOrigin-RevId: 167766751

* [TF:XLA] In Literal: correctly handle operands with zero elements in
Copy.

PiperOrigin-RevId: 167769308

* Reduce batch size for resampler backward pass test, to speed up test.

PiperOrigin-RevId: 167769539

* Remove `SimpleGraphExecutionState::costs_`, which is unused.

PiperOrigin-RevId: 167772120

* detecting cycles when users add a control edge to a graph

PiperOrigin-RevId: 167773598

* Make writer_test avoid setting content to a string

That content field of the PluginData proto is going to be converted into a bytes field, and setting it to a string makes the test fail. Furthermore, the purpose of this test is to make sure that correct data is written, so setting the name of the plugin suffices.

PiperOrigin-RevId: 167776457

* Propagate the original stack trace when exceptions caught be MonitoredSession
are re-raised.

PiperOrigin-RevId: 167781071

* Change trace.py to not access a graph as a default argument.

Checks for None and access via default graph inside the function.

PiperOrigin-RevId: 167788815

* Added custom metric support for tf.estimator.Estimator.

PiperOrigin-RevId: 167788891

* A eager Saver that allows restore on create.

PiperOrigin-RevId: 167789332

* Make content field of PluginData a bytes field

The content field had previously been a string field, which had been problematic because
string fields can only store UTF-8 strings.

This problem can manifest in various ways. For instance, take the precision-recall curve plugin. Its summary collects data that scales in size based on the number of thresholds. When the content field is a string, the summary logic serializes the relevant data proto just fine when we only have a few thresholds (about 100). However, for large numbers of thresholds (ie, around 200), the summary logic fails to serialize and throws a cryptic error.

ValueError: '\x10\xc8\x01' has type str, but isn't valid UTF-8 encoding. Non-UTF-8 strings must be converted to unicode objects before being added.

Changing the content field to a bytes field fixes this issue because bytes fields are not restricted to UTF-8 strings. I just happened to have needed a long enough string for the string to no longer be a valid UTF-8 one.

PiperOrigin-RevId: 167790594

* Temporarily disable tf_should_use wrapper, since it can cause
python Graph/Operation/Tensor memory leaks.

PiperOrigin-RevId: 167790657

* Ensure using "path" as a URI will keep working.

PiperOrigin-RevId: 167793848

* Fix typo in graph transforms error message

PiperOrigin-RevId: 167796563

* Merge changes from github.
END_PUBLIC

---
Commit 607816029 authored by Eugene Brevdo<ebrevdo@google.com>
Committed by TensorFlower Gardener<gardener@tensorflow.org>:
Extended ScratchSpace to expose its underlying scratch tensor object.

PiperOrigin-RevId: 167649551

---
Commit db43fe68e authored by A. Unique TensorFlower<gardener@tensorflow.org>
Committed by TensorFlower Gardener<gardener@tensorflow.org>:
Add fast math attributes to all generated methods when fast math enabled.

RELNOTES: n/a
PiperOrigin-RevId: 167646637

---
Commit aebe8cc6f authored by A. Unique TensorFlower<gardener@tensorflow.org>
Committed by TensorFlower Gardener<gardener@tensorflow.org>:
Call HloComputation.Accept instead of HloInstruction.Accept to get all instructions profiled.

RELNOTES: n/a
PiperOrigin-RevId: 167640259

---
Commit 0ab137cd8 authored by A. Unique TensorFlower<gardener@tensorflow.org>
Committed by TensorFlower Gardener<gardener@tensorflow.org>:
BEGIN_PUBLIC
Automated g4 rollback of changelist 167604306

PiperOrigin-RevId: 167800256

* Update ops-related pbtxt files.

PiperOrigin-RevId: 167802521

* Go: Update generated wrapper functions for TensorFlow ops.

PiperOrigin-RevId: 167804076

* Add sloppy_interleave dataset operator.

When feeding data at high speed into a model from variable-latency data
sources, head-of-line blocking can be a significant concern when using a
deterministic input pipeline, such as interleave.

This change introduces a new non-deterministic dataset operator that avoids
head-of-line blocking.

PiperOrigin-RevId: 167810743

* Update ops-related pbtxt files.

PiperOrigin-RevId: 167811375

* tfdbg: Fix python3 breakage in grpc debug tests caused by bytes-type plugin_data content

PiperOrigin-RevId: 167812508

* [XLA] Rip CheckFusionNode() out of instruction, and move it into the HLO verifier instead.

CheckFusionNode() is linear in the size of the fusion node, and was called once per Fuse(), leading to run-time quadratic in the fusion node's size.

PiperOrigin-RevId: 167812735

* Disable
tensorflow/contrib/data/python/kernel_tests/sloppy_transformation_dataset_op_test.py
in cmake.
2017-09-06 23:52:56 -07:00

459 lines
17 KiB
C++

/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/compiler/xla/service/hlo_verifier.h"
#include "tensorflow/compiler/xla/service/shape_inference.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/gtl/flatmap.h"
namespace xla {
namespace {
// Visitor which verifies that the output shape is correctly set. Verifies
// against the inferred shape for the instruction.
// TODO(b/26024837): Check output shape for all instruction types.
class ShapeVerifier : public DfsHloVisitor {
public:
explicit ShapeVerifier(
const std::function<int64(const Shape&)>& shape_size_fn)
: shape_size_fn_(shape_size_fn) {}
Status HandleElementwiseUnary(HloInstruction* hlo) override {
return CheckUnaryShape(hlo);
}
Status HandleElementwiseBinary(HloInstruction* hlo) override {
return CheckBinaryShape(hlo);
}
Status HandleClamp(HloInstruction* clamp, HloInstruction* min,
HloInstruction* arg, HloInstruction* max) override {
return CheckTernaryShape(clamp);
}
Status HandleSelect(HloInstruction* select, HloInstruction* pred,
HloInstruction* on_true,
HloInstruction* on_false) override {
return CheckTernaryShape(select);
}
Status HandleConcatenate(
HloInstruction* concatenate,
tensorflow::gtl::ArraySlice<HloInstruction*> operands) override {
return tensorflow::Status::OK();
}
Status HandleConvert(HloInstruction* convert) override {
return tensorflow::Status::OK();
}
Status HandleCopy(HloInstruction* copy) override {
return CheckUnaryShape(copy);
}
Status HandleDot(HloInstruction* dot, HloInstruction* lhs,
HloInstruction* rhs) override {
return CheckBinaryShape(dot);
}
Status HandleConvolution(HloInstruction* convolution, HloInstruction* lhs,
HloInstruction* rhs, const Window& window) override {
return tensorflow::Status::OK();
}
Status HandleCrossReplicaSum(HloInstruction* crs) override {
return tensorflow::Status::OK();
}
Status HandleReducePrecision(HloInstruction* reduce_precision) override {
return tensorflow::Status::OK();
}
Status HandleInfeed(HloInstruction* infeed) override {
return tensorflow::Status::OK();
}
Status HandleOutfeed(HloInstruction* outfeed) override {
return tensorflow::Status::OK();
}
Status HandleRng(HloInstruction* random,
RandomDistribution distribution) override {
return tensorflow::Status::OK();
}
Status HandleReverse(HloInstruction* reverse,
HloInstruction* operand) override {
return tensorflow::Status::OK();
}
Status HandleSort(HloInstruction* sort, HloInstruction* operand) override {
return tensorflow::Status::OK();
}
Status HandleConstant(HloInstruction* constant,
const Literal& literal) override {
return tensorflow::Status::OK();
}
Status HandleGetTupleElement(HloInstruction* get_tuple_element,
HloInstruction* operand) override {
return tensorflow::Status::OK();
}
Status HandleReduce(HloInstruction* reduce, HloInstruction* arg,
HloInstruction* init_value,
tensorflow::gtl::ArraySlice<int64> dimensions,
HloComputation* function) override {
return tensorflow::Status::OK();
}
Status HandleBitcast(HloInstruction* bitcast) override {
// Bitcasts can be any shape, as long as the size matches the operand size.
TF_RET_CHECK(shape_size_fn_(bitcast->shape()) ==
shape_size_fn_(bitcast->operand(0)->shape()));
return tensorflow::Status::OK();
}
Status HandleBroadcast(HloInstruction* broadcast) override {
TF_RET_CHECK(ShapeUtil::Rank(broadcast->operand(0)->shape()) ==
broadcast->dimensions().size());
return tensorflow::Status::OK();
}
Status HandleReshape(HloInstruction* reshape) override {
return tensorflow::Status::OK();
}
Status HandleTranspose(HloInstruction* transpose) override {
return tensorflow::Status::OK();
}
Status HandleParameter(HloInstruction* parameter) override {
return tensorflow::Status::OK();
}
Status HandleFusion(HloInstruction* fusion) override {
return tensorflow::Status::OK();
}
Status HandleCall(HloInstruction* call) override {
return tensorflow::Status::OK();
}
Status HandleCustomCall(HloInstruction* custom_call,
tensorflow::gtl::ArraySlice<HloInstruction*> operands,
tensorflow::StringPiece custom_call_target) override {
return tensorflow::Status::OK();
}
Status HandleSlice(HloInstruction* slice, HloInstruction* operand) override {
return tensorflow::Status::OK();
}
Status HandleDynamicSlice(HloInstruction* dynamic_slice,
HloInstruction* operand,
HloInstruction* start_indices) override {
return tensorflow::Status::OK();
}
Status HandleDynamicUpdateSlice(HloInstruction* dynamic_update_slice,
HloInstruction* operand,
HloInstruction* update,
HloInstruction* start_indices) override {
return tensorflow::Status::OK();
}
Status HandleTuple(
HloInstruction* tuple,
tensorflow::gtl::ArraySlice<HloInstruction*> operands) override {
return CheckVariadicShape(tuple);
}
Status HandleMap(
HloInstruction* map,
tensorflow::gtl::ArraySlice<HloInstruction*> operands,
HloComputation* function,
tensorflow::gtl::ArraySlice<HloInstruction*> static_operands) override {
return tensorflow::Status::OK();
}
Status HandleReduceWindow(HloInstruction* reduce_window,
HloInstruction* operand, const Window& window,
HloComputation* function) override {
return tensorflow::Status::OK();
}
Status HandleSelectAndScatter(HloInstruction* instruction) override {
return tensorflow::Status::OK();
}
Status HandleWhile(HloInstruction* xla_while) override {
return tensorflow::Status::OK();
}
Status HandlePad(HloInstruction* pad) override {
return tensorflow::Status::OK();
}
Status HandleSend(HloInstruction* send) override {
return tensorflow::Status::OK();
}
Status HandleRecv(HloInstruction* recv) override {
return tensorflow::Status::OK();
}
Status HandleBatchNormTraining(HloInstruction* batchNormTraining) override {
return tensorflow::Status::OK();
}
Status HandleBatchNormInference(HloInstruction* batchNormInference) override {
return tensorflow::Status::OK();
}
Status HandleBatchNormGrad(HloInstruction* batchNormGrad) override {
return tensorflow::Status::OK();
}
Status FinishVisit(HloInstruction* root) override {
return tensorflow::Status::OK();
}
private:
// Check the instruction's shape against the given expected shape and return
// an appropriate error if there is a mismatch.
Status CheckShape(const HloInstruction* instruction,
const Shape& expected_shape) {
if (!ShapeUtil::Compatible(instruction->shape(), expected_shape)) {
return InvalidArgument(
"Expected instruction to have shape compatible with %s, actual "
"shape is %s:\n%s",
ShapeUtil::HumanString(expected_shape).c_str(),
ShapeUtil::HumanString(instruction->shape()).c_str(),
instruction->ToString().c_str());
}
return tensorflow::Status::OK();
}
// Check a unary (binary, etc) instruction's shape against the inferred shape.
Status CheckUnaryShape(const HloInstruction* instruction) {
TF_ASSIGN_OR_RETURN(const Shape expected,
ShapeInference::InferUnaryOpShape(
instruction->opcode(), instruction->operand(0)));
return CheckShape(instruction, expected);
}
Status CheckBinaryShape(const HloInstruction* instruction) {
TF_ASSIGN_OR_RETURN(const Shape expected,
ShapeInference::InferBinaryOpShape(
instruction->opcode(), instruction->operand(0),
instruction->operand(1)));
return CheckShape(instruction, expected);
}
Status CheckTernaryShape(const HloInstruction* instruction) {
TF_ASSIGN_OR_RETURN(const Shape expected,
ShapeInference::InferTernaryOpShape(
instruction->opcode(), instruction->operand(0),
instruction->operand(1), instruction->operand(2)));
return CheckShape(instruction, expected);
}
Status CheckVariadicShape(const HloInstruction* instruction) {
TF_ASSIGN_OR_RETURN(const Shape expected,
ShapeInference::InferVariadicOpShape(
instruction->opcode(), instruction->operands()));
return CheckShape(instruction, expected);
}
// Returns the size of a Shape in bytes.
const std::function<int64(const Shape&)> shape_size_fn_;
};
string ComputationsToString(
tensorflow::gtl::ArraySlice<HloComputation*> computations) {
return tensorflow::str_util::Join(
computations, ",", [](string* s, const HloComputation* computation) {
s->append(computation->name());
});
}
} // namespace
Status HloVerifier::CheckFusionInstruction(HloInstruction* fusion) const {
// The parent fusion instruction of the fusion computation must be 'fusion'.
HloComputation* fused_computation = fusion->fused_instructions_computation();
if (fusion != fused_computation->FusionInstruction()) {
return FailedPrecondition(
"Instruction of fused computation does not match expected instruction "
"%s.",
fusion->ToString().c_str());
}
// Fused root instruction and fused parameters must all be owned by the fusion
// computation.
bool root_owned = false;
const std::vector<HloInstruction*>& fused_parameters =
fusion->fused_parameters();
const HloInstruction* fused_root = fusion->fused_expression_root();
std::vector<bool> parameter_owned(fused_parameters.size(), false);
for (auto& instruction : fused_computation->instructions()) {
if (fused_root == instruction.get()) {
if (root_owned) {
return FailedPrecondition("Root appears more than once in %s.",
fusion->ToString().c_str());
}
root_owned = true;
}
for (int i = 0; i < fused_parameters.size(); ++i) {
if (fused_parameters[i] == instruction.get()) {
if (parameter_owned[i]) {
return FailedPrecondition("Parameter appears more than once in %s.",
fusion->ToString().c_str());
}
parameter_owned[i] = true;
}
}
}
if (!root_owned) {
return FailedPrecondition("Root not found in computation of %s.",
fusion->ToString().c_str());
}
// Make sure all the parameter_owned entries are set
for (int i = 0; i < parameter_owned.size(); i++) {
if (!parameter_owned[i]) {
return FailedPrecondition("Parameter %d not found in computation of %s.",
i, fusion->ToString().c_str());
}
}
// Fused root must have no users.
if (fused_root->user_count() != 0) {
return FailedPrecondition("Root of %s may not have users.",
fusion->ToString().c_str());
}
// All uses of fused instructions must be in the fusion computation, and every
// non-root instruction must have at least one use.
for (auto& instruction :
fusion->fused_instructions_computation()->instructions()) {
if (instruction.get() != fused_root) {
if (instruction->user_count() == 0) {
return FailedPrecondition(
"Non-root instruction %s in %s must have users.",
instruction->ToString().c_str(), fusion->ToString().c_str());
}
for (auto& user : instruction->users()) {
if (fused_computation != user->parent()) {
return FailedPrecondition(
"Non-root instruction %s in %s may not have external users.",
instruction->ToString().c_str(), fusion->ToString().c_str());
}
}
}
}
// Fused parameter instructions must be numbered contiguously and match up
// (shapes compatible) with their respective operand.
CHECK_EQ(fusion->operands().size(), fused_parameters.size());
std::vector<bool> parameter_numbers(fused_parameters.size(), false);
for (auto fused_param : fused_parameters) {
int64 param_no = fused_param->parameter_number();
if (param_no < 0) {
return FailedPrecondition(
"Unexpected negative parameter number %lld in %s.", param_no,
fusion->ToString().c_str());
}
if (param_no >= fused_parameters.size()) {
return FailedPrecondition(
"Unexpected parameter number %lld in %s: higher then number of "
"parameters %lu.",
param_no, fusion->ToString().c_str(), fused_parameters.size());
}
if (parameter_numbers[param_no]) {
return FailedPrecondition(
"Did not expect parameter number %lld more than once in %s.",
param_no, fusion->ToString().c_str());
}
parameter_numbers[param_no] = true;
if (!ShapeUtil::Compatible(fused_param->shape(),
fusion->operand(param_no)->shape())) {
return FailedPrecondition(
"Shape mismatch between parameter number %lld and its operand in %s.",
param_no, fusion->ToString().c_str());
}
}
// Make sure all the parameter_numbers entries were seen
for (int i = 0; i < parameter_numbers.size(); i++) {
if (!parameter_numbers[i]) {
return FailedPrecondition("Did not see parameter number %d in %s.", i,
fusion->ToString().c_str());
}
}
// TODO(b/65423525): We'd like to check that all operands are distinct.
// This is currently disabled due to the invariant being violated by
// multi-output fusion.
return tensorflow::Status::OK();
}
StatusOr<bool> HloVerifier::Run(HloModule* module) {
tensorflow::gtl::FlatMap<string, const HloInstruction*> instructions;
ShapeVerifier shape_verifier(shape_size_fn_);
for (auto& computation : module->computations()) {
for (const auto& instruction : computation->instructions()) {
TF_RET_CHECK(instruction->parent() == computation.get());
if (instruction->opcode() == HloOpcode::kFusion) {
TF_RETURN_IF_ERROR(CheckFusionInstruction(instruction.get()));
TF_RET_CHECK(
ContainersEqual(instruction->called_computations(),
{instruction->fused_instructions_computation()}))
<< "Fusion HLO calls computations other than the "
"fused_instructions_computation: "
<< instruction->ToString()
<< " instruction->fused_instructions_computation(): "
<< instruction->fused_instructions_computation()->ToString()
<< " instruction->called_computations(): "
<< ComputationsToString(instruction->called_computations());
for (const auto& fused : instruction->fused_instructions()) {
TF_RET_CHECK(fused->parent() ==
instruction->fused_instructions_computation())
<< "Fused HLO was missing a parent: " << fused->ToString()
<< " parent: " << fused->parent()
<< " computation: " << computation.get();
}
}
auto previous = instructions.find(instruction->name());
TF_RET_CHECK(previous == instructions.end())
<< "HLO has name that is not unique within module:\n"
<< instruction->ToString()
<< " in computation: " << computation->name()
<< "\nPrevious HLO with same name:\n"
<< previous->second->ToString()
<< " in computation: " << previous->second->parent()->name();
instructions[instruction->name()] = instruction.get();
}
TF_RETURN_IF_ERROR(computation->Accept(&shape_verifier));
}
return false;
}
} // namespace xla