diff options
author | 2017-11-09 17:55:08 -0800 | |
---|---|---|
committer | 2017-11-10 16:14:41 -0800 | |
commit | 8392a4b8e9d6d7ccbfde15dcdda0477c2791b6dc (patch) | |
tree | 2f4ccc8b42f2cbba82ebc2365d2e7a1556d24435 | |
parent | f9c7db10e294a011baf4d168e80c8209efb57f72 (diff) |
Hlo parser: support padding.
Also, give PaddingConfig its own ToString format.
PiperOrigin-RevId: 175239832
-rw-r--r-- | tensorflow/compiler/xla/service/hlo_instruction.cc | 18 | ||||
-rw-r--r-- | tensorflow/compiler/xla/service/hlo_instruction.h | 2 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/README.md | 6 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/hlo_lexer.cc | 13 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/hlo_lexer.h | 2 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/hlo_parser.cc | 51 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/hlo_parser_test.cc | 52 | ||||
-rw-r--r-- | tensorflow/compiler/xla/tools/parser/hlo_token.h | 2 |
8 files changed, 130 insertions, 16 deletions
diff --git a/tensorflow/compiler/xla/service/hlo_instruction.cc b/tensorflow/compiler/xla/service/hlo_instruction.cc index 1b2161fc2e..674d3e3836 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.cc +++ b/tensorflow/compiler/xla/service/hlo_instruction.cc @@ -1889,7 +1889,8 @@ std::vector<string> HloInstruction::ExtraAttributesToString() const { extra.push_back(StrCat("window={", window_util::ToString(*window_), "}")); } if (padding_config_ != nullptr) { - extra.push_back(StrCat("padding=", padding_config_->ShortDebugString())); + extra.push_back( + StrCat("padding=", xla::PaddingConfigToString(*padding_config_))); } if (opcode() == HloOpcode::kSlice) { std::vector<string> bounds; @@ -2894,6 +2895,21 @@ StatusOr<HloInstruction::FusionKind> StringToFusionKind( return InvalidArgument("Unknown fusion kind: %s", kind_name.c_str()); } +string PaddingConfigToString(const PaddingConfig& padding) { + bool has_interior_padding = + std::any_of(padding.dimensions().begin(), padding.dimensions().end(), + [](const PaddingConfig::PaddingConfigDimension& dim) { + return dim.interior_padding() != 0; + }); + return Join( + padding.dimensions(), "x", + [&](string* out, const PaddingConfig::PaddingConfigDimension& dim) { + StrAppend( + out, dim.edge_padding_low(), "_", dim.edge_padding_high(), + has_interior_padding ? StrCat("_", dim.interior_padding()) : ""); + }); +} + std::ostream& operator<<(std::ostream& os, HloInstruction::FusionKind kind) { return os << ToString(kind); } diff --git a/tensorflow/compiler/xla/service/hlo_instruction.h b/tensorflow/compiler/xla/service/hlo_instruction.h index 974d43d89e..64a88164a7 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.h +++ b/tensorflow/compiler/xla/service/hlo_instruction.h @@ -1234,6 +1234,8 @@ string ToString(HloInstruction::FusionKind kind); StatusOr<HloInstruction::FusionKind> StringToFusionKind( const string& kind_name); +string PaddingConfigToString(const PaddingConfig& padding); + std::ostream& operator<<(std::ostream& os, HloInstruction::FusionKind kind); // Map classes that guarantee a deterministic iteration order when the key is diff --git a/tensorflow/compiler/xla/tools/parser/README.md b/tensorflow/compiler/xla/tools/parser/README.md index 986041caf6..b768b94e77 100644 --- a/tensorflow/compiler/xla/tools/parser/README.md +++ b/tensorflow/compiler/xla/tools/parser/README.md @@ -54,9 +54,9 @@ attribute attribute_value : kInt | kName - | [0-9bf]{3,}_[0-9io]{3,}->[0-9bf]{3,} /*dim_labels_pattern*/ - | [0-9]+(x[0-9]+)+ /*dxd_pattern*/ - | [0-9]+_[0-9]+(x[0-9]+_[0-9]+)* /*window_pad_pattern*/ + | [0-9bf]{3,}_[0-9io]{3,}->[0-9bf]{3,} /*dim_labels_pattern*/ + | [0-9]+(x[0-9]+)+ /*dxd_pattern*/ + | [0-9]+_[0-9]+(_[0-9]+)?(x[0-9]+_[0-9]+(_[0-9]+)?)* /*pad_pattern*/ | '{' sub_attributes '}' ; diff --git a/tensorflow/compiler/xla/tools/parser/hlo_lexer.cc b/tensorflow/compiler/xla/tools/parser/hlo_lexer.cc index f70386411c..b5befbf58b 100644 --- a/tensorflow/compiler/xla/tools/parser/hlo_lexer.cc +++ b/tensorflow/compiler/xla/tools/parser/hlo_lexer.cc @@ -254,13 +254,13 @@ TokKind HloLexer::LexPercent() { } // Lex integer and floating-point values, -inf, and patterns for dim labels, -// dxd (e.g. 1x2x3), and window pad. +// dxd (e.g. 1x2x3), and pad. // // fp with exp ::= [-]?([0-9]+|[0-9]+[.][0-9]*|[0-9]*[.][0-9]+)([eE][+-]?[0-9]+) // fp without exp ::= [-]?([0-9]+[.][0-9]*|[0-9]*[.][0-9]+) // dim_labels_pattern ::= [0-9bf]{3,}_[0-9io]{3,}->[0-9bf]{3,} // dxd_pattern ::= [0-9]+(x[0-9]+)+ -// window_pad_pattern ::= [0-9]+_[0-9]+(x[0-9]+_[0-9]+)* +// pad_pattern ::= [0-9]+_[0-9]+(_[0-9]+)?(x[0-9]+_[0-9]+(_[0-9]+)?)* // int ::= [-]?[0-9]+ // negative inf ::= '-inf' TokKind HloLexer::LexNumberOrPattern() { @@ -277,7 +277,8 @@ TokKind HloLexer::LexNumberOrPattern() { static LazyRE2 dim_labels_pattern = { R"([0-9bf]{3,}_[0-9io]{3,}->[0-9bf]{3,})"}; static LazyRE2 dxd_pattern = {R"([0-9]+(x[0-9]+)+)"}; - static LazyRE2 pad_pattern = {R"([0-9]+_[0-9]+(x[0-9]+_[0-9]+)*)"}; + static LazyRE2 pad_pattern = { + R"([0-9]+_[0-9]+(_[0-9]+)?(x[0-9]+_[0-9]+(_[0-9]+)?)*)"}; if (RE2::Consume(&consumable, *dim_labels_pattern)) { current_ptr_ = consumable.begin(); @@ -294,7 +295,7 @@ TokKind HloLexer::LexNumberOrPattern() { if (RE2::Consume(&consumable, *pad_pattern)) { current_ptr_ = consumable.begin(); str_val_.assign(token_start_, current_ptr_); - return TokKind::kWindowPad; + return TokKind::kPad; } static LazyRE2 int_pattern = {R"([-]?\d+)"}; @@ -395,8 +396,8 @@ string TokKindToString(TokKind kind) { return "kDimLabels"; case TokKind::kDxD: return "kDxD"; - case TokKind::kWindowPad: - return "kWindowPad"; + case TokKind::kPad: + return "kPad"; case TokKind::kShape: return "kShape"; case TokKind::kOpcode: diff --git a/tensorflow/compiler/xla/tools/parser/hlo_lexer.h b/tensorflow/compiler/xla/tools/parser/hlo_lexer.h index 74e6829180..79c4f271a1 100644 --- a/tensorflow/compiler/xla/tools/parser/hlo_lexer.h +++ b/tensorflow/compiler/xla/tools/parser/hlo_lexer.h @@ -45,7 +45,7 @@ class HloLexer { case TokKind::kAttributeName: case TokKind::kDimLabels: case TokKind::kDxD: - case TokKind::kWindowPad: + case TokKind::kPad: return str_val_; default: LOG(FATAL) << "This token does not have string value"; diff --git a/tensorflow/compiler/xla/tools/parser/hlo_parser.cc b/tensorflow/compiler/xla/tools/parser/hlo_parser.cc index 710877b4e0..fed0492a54 100644 --- a/tensorflow/compiler/xla/tools/parser/hlo_parser.cc +++ b/tensorflow/compiler/xla/tools/parser/hlo_parser.cc @@ -99,6 +99,7 @@ class HloParser { kSharding, kInstructionList, kSliceRanges, + kPaddingConfig, }; struct AttrConfig { @@ -134,6 +135,7 @@ class HloParser { bool ParseInstructionNames(std::vector<HloInstruction*>* instructions); bool ParseWindow(Window* window); bool ParseConvolutionDimensionNumbers(ConvolutionDimensionNumbers* dnums); + bool ParsePaddingConfig(PaddingConfig* padding); bool ParseSharding(OpSharding* sharding); bool ParseSingleSharding(OpSharding* sharding, bool lbrace_pre_lexed); @@ -727,9 +729,19 @@ bool HloParser::ParseInstruction(HloComputation::Builder* builder, /*grad_output=*/operands[4], *epsilon, *feature_index)); break; } + case HloOpcode::kPad: { + optional<PaddingConfig> padding; + attrs["padding"] = {/*required=*/true, AttrTy::kPaddingConfig, &padding}; + if (!ParseOperands(&operands, /*expected_size=*/2) || + !ParseAttributes(attrs)) { + return false; + } + instruction = builder->AddInstruction(HloInstruction::CreatePad( + shape, operands[0], /*padding_value=*/operands[1], *padding)); + break; + } case HloOpcode::kCustomCall: case HloOpcode::kReducePrecision: - case HloOpcode::kPad: case HloOpcode::kRng: case HloOpcode::kFusion: case HloOpcode::kInfeed: @@ -1374,6 +1386,14 @@ bool HloParser::ParseAttributes( static_cast<optional<SliceRanges>*>(attr_out_ptr)->emplace(result); return true; } + case AttrTy::kPaddingConfig: { + PaddingConfig result; + if (!ParsePaddingConfig(&result)) { + return false; + } + static_cast<optional<PaddingConfig>*>(attr_out_ptr)->emplace(result); + return true; + } } }(); if (!success) { @@ -1774,7 +1794,7 @@ bool HloParser::ParseWindowPad(std::vector<std::vector<int64>>* pad) { if (!pad->empty()) { return TokenError("sub-attribute 'pad=' already exists"); } - if (lexer_.GetKind() != TokKind::kWindowPad) { + if (lexer_.GetKind() != TokKind::kPad) { return TokenError("expects window pad pattern, e.g., '0_0x3_3'"); } string str = lexer_.GetStrVal(); @@ -1792,6 +1812,33 @@ bool HloParser::ParseWindowPad(std::vector<std::vector<int64>>* pad) { return true; } +// This is the inverse xla::ToString(PaddingConfig). The padding config string +// looks like "0_0_0x3_3_1". The string is first separated by 'x', each +// substring represents one PaddingConfigDimension. The substring is 3 (or 2) +// numbers joined by '_'. +bool HloParser::ParsePaddingConfig(PaddingConfig* padding) { + if (lexer_.GetKind() != TokKind::kPad) { + return TokenError("expects padding config, e.g., '0_0_0x3_3_1'"); + } + string str = lexer_.GetStrVal(); + std::vector<string> padding_str = Split(str, 'x'); + for (const auto& padding_dim_str : padding_str) { + std::vector<int64> padding_dim; + if (!SplitAndParseAsInts(padding_dim_str, '_', &padding_dim) || + (padding_dim.size() != 2 && padding_dim.size() != 3)) { + return TokenError( + "expects padding config pattern like 'low_high_interior' or " + "'low_high'"); + } + auto* dim = padding->add_dimensions(); + dim->set_edge_padding_low(padding_dim[0]); + dim->set_edge_padding_high(padding_dim[1]); + dim->set_interior_padding(padding_dim.size() == 3 ? padding_dim[2] : 0); + } + lexer_.Lex(); + return true; +} + bool HloParser::ParseOpcode(HloOpcode* result) { VLOG(1) << "ParseOpcode"; if (lexer_.GetKind() != TokKind::kOpcode) { diff --git a/tensorflow/compiler/xla/tools/parser/hlo_parser_test.cc b/tensorflow/compiler/xla/tools/parser/hlo_parser_test.cc index fbe0409e3d..d19c6e1877 100644 --- a/tensorflow/compiler/xla/tools/parser/hlo_parser_test.cc +++ b/tensorflow/compiler/xla/tools/parser/hlo_parser_test.cc @@ -36,6 +36,10 @@ string TestDataToString(const ::testing::TestParamInfo<TestData>& data) { return data.param.test_name; } +// For each string below, we check that: +// - we parse it to an HloModule successfully, and +// - the stringification of the resulting HloModule is equal to our original +// string. std::vector<TestData> CreateTestCases() { // clang-format off return std::vector<TestData>({ @@ -510,6 +514,32 @@ ENTRY %BatchNormGrad.v4 (input: f32[2,2,2,2], scale: f32[2], mean: f32[2], varia } )" +}, +// pad +{ +"Pad", +R"(HloModule Pad1DS3Array_module: + +ENTRY %Pad1DS3Array.v3 () -> f32[8] { + %constant = f32[3]{0} constant({1, 2, 3}) + %constant.1 = f32[] constant(0.1) + ROOT %pad = f32[8]{0} pad(f32[3]{0} %constant, f32[] %constant.1), padding=3_1 +} + +)" +}, +// pad has interior +{ +"PadHasInterior", +R"(HloModule PadHasInterior_module: + +ENTRY %PadHasInterior.v3 (input: f32[1,25,7,7]) -> f32[1,25,17,11] { + %input = f32[1,25,7,7]{3,2,1,0} parameter(0) + %constant = f32[] constant(-5.123) + ROOT %pad = f32[1,25,17,11]{3,2,1,0} pad(f32[1,25,7,7]{3,2,1,0} %input, f32[] %constant), padding=0_0_0x0_0_0x2_2_1x2_2_0 +} + +)" } }); // clang-format on @@ -523,7 +553,10 @@ class HloParserTest : public ::testing::Test, << "'" << s << "' does not contain '" << expected << "'"; } - void ExpectSuccess() { + // Expects "ToString(Parse(string)) == string", that is, parses the string, + // asserts that it succeeded, stringifies the parsed module, and checks that + // the it equals the original string. + void ExpectEqual() { const string& original = GetParam().module_string; auto result = Parse(original); TF_EXPECT_OK(result.status()); @@ -532,7 +565,7 @@ class HloParserTest : public ::testing::Test, } }; -TEST_P(HloParserTest, Run) { ExpectSuccess(); } +TEST_P(HloParserTest, Run) { ExpectEqual(); } INSTANTIATE_TEST_CASE_P(HloParserTestSuccessInstantiation, HloParserTest, ::testing::ValuesIn(CreateTestCases()), @@ -793,6 +826,21 @@ ENTRY %slice.v2 (p0: f32[3,3,4,4]) -> f32[3,3,2,4] { TF_EXPECT_OK(Parse(original).status()); } +TEST_F(HloParserTest, PaddingConfigIsNotWindowPad) { + const string original = R"(HloModule window_pad_module: + +ENTRY %Convolve1D1Window_0.v3 (input: f32[1,2,1], filter: f32[1,1,1]) -> f32[1,2,1] { + %input = f32[1,2,1]{2,1,0} parameter(0) + %copy = f32[1,2,1]{2,0,1} copy(f32[1,2,1]{2,1,0} %input) + %filter = f32[1,1,1]{2,1,0} parameter(1) + ROOT %convolution = f32[1,2,1]{2,0,1} convolution(f32[1,2,1]{2,0,1} %copy, f32[1,1,1]{2,1,0} %filter), dim_labels=b0f_0io->b0f, window={pad=1_1_0 size=1} +} + +)"; + ExpectHasSubstr(Parse(original).status().error_message(), + "expects padding_low and padding_high separated by '_'"); +} + } // namespace } // namespace tools } // namespace xla diff --git a/tensorflow/compiler/xla/tools/parser/hlo_token.h b/tensorflow/compiler/xla/tools/parser/hlo_token.h index 15ab8b1ccc..9afd2fac23 100644 --- a/tensorflow/compiler/xla/tools/parser/hlo_token.h +++ b/tensorflow/compiler/xla/tools/parser/hlo_token.h @@ -59,7 +59,7 @@ enum class TokKind { kAttributeName, // dimensions= kDimLabels, // [0-9bf]+_[0-9io]+->[0-9bf]+ kDxD, // [0-9]+(x[0-9]+)+ - kWindowPad, // [0-9]+_[0-9]+(x[0-9]+_[0-9]+)* + kPad, // [0-9]+_[0-9]+(_[0-9]+)?(x[0-9]+_[0-9]+(_[0-9]+)?)* kShape, // f32[2,3]{1,0} kOpcode, // add kInt, // 42 |