aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Sanjoy Das <sanjoy@google.com>2018-05-09 19:39:58 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-05-09 19:42:49 -0700
commitbb8315f0cf066266647c6eacdf575ac8f5e9989e (patch)
tree3701a5004258519f0baa4420416008be22dc0114
parentf79dbc73c5b2c0debb916280e4436d98890ed03b (diff)
Don't call into Eigen unless the input and output tensors are aligned
We teach TargetMachineFeatures about the alignment required for Eigen GEMM and Conv and then pipe TargetMachineFeatures through the places that need to decide whether a dot or a conv needs to be lowered to a call to Eigen. I also had to fix a minor bug in our LLVM IR implementation for convolution. PiperOrigin-RevId: 196065557
-rw-r--r--tensorflow/compiler/xla/service/cpu/BUILD32
-rw-r--r--tensorflow/compiler/xla/service/cpu/conv_canonicalization.cc3
-rw-r--r--tensorflow/compiler/xla/service/cpu/conv_canonicalization.h8
-rw-r--r--tensorflow/compiler/xla/service/cpu/conv_canonicalization_test.cc13
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_compiler.cc37
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_compiler.h4
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc94
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc6
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.h9
-rw-r--r--tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc15
-rw-r--r--tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc40
-rw-r--r--tensorflow/compiler/xla/service/cpu/dot_op_emitter.h4
-rw-r--r--tensorflow/compiler/xla/service/cpu/ir_emission_utils.cc32
-rw-r--r--tensorflow/compiler/xla/service/cpu/ir_emission_utils.h9
-rw-r--r--tensorflow/compiler/xla/service/cpu/ir_emission_utils_test.cc8
-rw-r--r--tensorflow/compiler/xla/service/cpu/ir_emitter.cc48
-rw-r--r--tensorflow/compiler/xla/service/cpu/ir_emitter.h7
-rw-r--r--tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc13
-rw-r--r--tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h13
-rw-r--r--tensorflow/compiler/xla/service/cpu/parallel_task_assignment_test.cc30
-rw-r--r--tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc25
-rw-r--r--tensorflow/compiler/xla/service/cpu/simple_orc_jit.h6
-rw-r--r--tensorflow/compiler/xla/service/cpu/target_machine_features.cc27
-rw-r--r--tensorflow/compiler/xla/service/cpu/target_machine_features.h55
-rw-r--r--tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h57
25 files changed, 476 insertions, 119 deletions
diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD
index 7e6d58c7fa..790163fca6 100644
--- a/tensorflow/compiler/xla/service/cpu/BUILD
+++ b/tensorflow/compiler/xla/service/cpu/BUILD
@@ -296,6 +296,15 @@ cc_library(
)
cc_library(
+ name = "target_machine_features_fake",
+ testonly = 1,
+ hdrs = ["target_machine_features_fake.h"],
+ deps = [
+ ":target_machine_features",
+ ],
+)
+
+cc_library(
name = "ir_function",
srcs = ["ir_function.cc"],
hdrs = ["ir_function.h"],
@@ -336,6 +345,7 @@ cc_library(
deps = [
":cpu_options",
":cpu_runtime",
+ ":ir_emission_utils",
":target_machine_features",
":vector_support_library",
"//tensorflow/compiler/xla:shape_util",
@@ -660,6 +670,7 @@ cc_library(
hdrs = ["ir_emission_utils.h"],
deps = [
":cpu_runtime",
+ ":target_machine_features",
"//tensorflow/compiler/xla:shape_util",
"//tensorflow/compiler/xla:window_util",
"//tensorflow/compiler/xla/service:hlo",
@@ -672,6 +683,7 @@ tf_cc_test(
srcs = ["ir_emission_utils_test.cc"],
deps = [
":ir_emission_utils",
+ ":target_machine_features_fake",
"//tensorflow/compiler/xla:test",
"//tensorflow/compiler/xla:test_helpers",
"//tensorflow/compiler/xla:util",
@@ -690,6 +702,7 @@ cc_library(
deps = [
":dot_op_emitter",
":ir_emission_utils",
+ ":target_machine_features",
"//tensorflow/compiler/xla:util",
"//tensorflow/compiler/xla/service:computation_layout",
"//tensorflow/compiler/xla/service:layout_assignment",
@@ -703,6 +716,7 @@ tf_cc_test(
srcs = ["cpu_layout_assignment_test.cc"],
deps = [
":cpu_layout_assignment",
+ ":target_machine_features_fake",
"//tensorflow/compiler/xla:literal_util",
"//tensorflow/compiler/xla:shape_layout",
"//tensorflow/compiler/xla:shape_util",
@@ -727,6 +741,7 @@ cc_library(
deps = [
":cpu_runtime",
":ir_emission_utils",
+ ":target_machine_features",
"//tensorflow/compiler/xla:shape_util",
"//tensorflow/compiler/xla:util",
"//tensorflow/compiler/xla:xla_data_proto",
@@ -741,6 +756,7 @@ tf_cc_test(
srcs = ["conv_canonicalization_test.cc"],
deps = [
":conv_canonicalization",
+ ":target_machine_features_fake",
"//tensorflow/compiler/xla:test",
"//tensorflow/compiler/xla:test_helpers",
"//tensorflow/compiler/xla:util",
@@ -779,6 +795,7 @@ cc_library(
":dot_op_emitter",
":ir_emission_utils",
":shape_partition",
+ ":target_machine_features",
"//tensorflow/compiler/xla/service:hlo",
"//tensorflow/compiler/xla/service:hlo_cost_analysis",
"//tensorflow/compiler/xla/service:hlo_pass",
@@ -791,6 +808,7 @@ tf_cc_test(
deps = [
":cpu_executable",
":parallel_task_assignment",
+ ":target_machine_features_fake",
"//tensorflow/compiler/xla:literal_util",
"//tensorflow/compiler/xla:shape_layout",
"//tensorflow/compiler/xla:shape_util",
@@ -913,3 +931,17 @@ tf_cc_test(
"//tensorflow/core:test",
],
)
+
+tf_cc_test(
+ name = "cpu_eigen_tensor_alignment_test",
+ size = "small",
+ srcs = ["cpu_eigen_tensor_alignment_test.cc"],
+ deps = [
+ ":dot_op_emitter",
+ ":ir_emission_utils",
+ ":target_machine_features_fake",
+ "//tensorflow/compiler/xla:test",
+ "//tensorflow/compiler/xla/tests:xla_internal_test_main",
+ "//tensorflow/compiler/xla/tools/parser:hlo_parser",
+ ],
+)
diff --git a/tensorflow/compiler/xla/service/cpu/conv_canonicalization.cc b/tensorflow/compiler/xla/service/cpu/conv_canonicalization.cc
index 2136aeb387..0985b9297f 100644
--- a/tensorflow/compiler/xla/service/cpu/conv_canonicalization.cc
+++ b/tensorflow/compiler/xla/service/cpu/conv_canonicalization.cc
@@ -33,7 +33,8 @@ StatusOr<bool> ConvCanonicalization::Run(HloModule* module) {
for (HloInstruction* hlo :
module->entry_computation()->MakeInstructionPostOrder()) {
if (hlo->opcode() == HloOpcode::kConvolution &&
- !PotentiallyImplementedAsEigenConvolution(*hlo)) {
+ !PotentiallyImplementedAsEigenConvolution(*hlo,
+ target_machine_features_)) {
const ConvolutionDimensionNumbers& dnums =
hlo->convolution_dimension_numbers();
auto input_batch_dim = dnums.input_batch_dimension();
diff --git a/tensorflow/compiler/xla/service/cpu/conv_canonicalization.h b/tensorflow/compiler/xla/service/cpu/conv_canonicalization.h
index 9b2c3d82eb..e6fd1499ed 100644
--- a/tensorflow/compiler/xla/service/cpu/conv_canonicalization.h
+++ b/tensorflow/compiler/xla/service/cpu/conv_canonicalization.h
@@ -16,6 +16,7 @@ limitations under the License.
#ifndef TENSORFLOW_COMPILER_XLA_SERVICE_CPU_CONV_CANONICALIZATION_H_
#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_CONV_CANONICALIZATION_H_
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
#include "tensorflow/compiler/xla/service/hlo_module.h"
#include "tensorflow/compiler/xla/service/hlo_pass_interface.h"
@@ -32,12 +33,19 @@ namespace cpu {
// convolutions can run faster.
class ConvCanonicalization : public HloPassInterface {
public:
+ explicit ConvCanonicalization(
+ const TargetMachineFeatures* target_machine_features)
+ : target_machine_features_(*target_machine_features) {}
+
~ConvCanonicalization() override {}
tensorflow::StringPiece name() const override {
return "convolution-canonicalization";
}
StatusOr<bool> Run(HloModule* module) override;
+
+ private:
+ const TargetMachineFeatures& target_machine_features_;
};
} // namespace cpu
diff --git a/tensorflow/compiler/xla/service/cpu/conv_canonicalization_test.cc b/tensorflow/compiler/xla/service/cpu/conv_canonicalization_test.cc
index 968f53d5c7..375b017b09 100644
--- a/tensorflow/compiler/xla/service/cpu/conv_canonicalization_test.cc
+++ b/tensorflow/compiler/xla/service/cpu/conv_canonicalization_test.cc
@@ -17,6 +17,7 @@ limitations under the License.
#include <vector>
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h"
#include "tensorflow/compiler/xla/service/hlo_computation.h"
#include "tensorflow/compiler/xla/service/hlo_instruction.h"
#include "tensorflow/compiler/xla/service/hlo_module.h"
@@ -89,7 +90,11 @@ TEST_F(ConvCanonicalizationTest, NonCanonicalToCanonical) {
HloComputation* entry_computation =
module->AddEntryComputation(builder.Build());
- ConvCanonicalization conv_canonicalization;
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features(
+ [](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+ ConvCanonicalization conv_canonicalization(&target_machine_features);
EXPECT_TRUE(conv_canonicalization.Run(module.get()).ValueOrDie());
const HloInstruction* output_reshape = entry_computation->root_instruction();
@@ -146,7 +151,11 @@ TEST_F(ConvCanonicalizationTest, CanonicalStaysTheSame) {
auto module = CreateNewModule();
module->AddEntryComputation(builder.Build());
- ConvCanonicalization conv_canonicalization;
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features(
+ [](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+ ConvCanonicalization conv_canonicalization(&target_machine_features);
EXPECT_FALSE(conv_canonicalization.Run(module.get()).ValueOrDie());
}
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc
index 3d2e24ca14..7c89debd6c 100644
--- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc
+++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc
@@ -231,7 +231,10 @@ class CollectProfileCandidates : public DfsHloVisitorWithDefault {
};
} // namespace
-Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile) {
+Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile,
+ llvm::TargetMachine* target_machine) {
+ LLVMTargetMachineFeatures target_machine_features(target_machine);
+
// Optimization pipeline.
HloPassPipeline pipeline("CPU");
pipeline.AddInvariantChecker<HloVerifier>();
@@ -249,7 +252,7 @@ Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile) {
// pass.
pipeline.AddPass<CallInliner>();
pipeline.AddPass<DotDecomposer>();
- pipeline.AddPass<ConvCanonicalization>();
+ pipeline.AddPass<ConvCanonicalization>(&target_machine_features);
{
auto& pass =
pipeline.AddPass<HloPassFix<HloPassPipeline>>("simplification");
@@ -279,9 +282,10 @@ Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile) {
pass.AddPass<ConditionalSimplifier>();
}
pipeline.AddPass<TransposeFolding>(
- [](const HloInstruction& dot,
- const TransposeFolding::OperandIndices& candidate_operands) {
- return PotentiallyImplementedAsEigenDot(dot)
+ [&target_machine_features](
+ const HloInstruction& dot,
+ const TransposeFolding::OperandIndices& candidate_operands) {
+ return PotentiallyImplementedAsEigenDot(dot, target_machine_features)
? candidate_operands
: TransposeFolding::OperandIndices{};
},
@@ -296,7 +300,7 @@ Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile) {
ReducePrecisionInsertion::PassTiming::AFTER_FUSION);
pipeline.AddPass<CpuLayoutAssignment>(
- module->device_entry_computation_layout());
+ module->device_entry_computation_layout(), &target_machine_features);
// The LayoutAssignment pass may leave behind kCopy instructions which are
// duplicate or NOPs, so remove them with algebraic simplification and CSE.
pipeline.AddPass<HloPassFix<AlgebraicSimplifier>>(
@@ -316,8 +320,8 @@ Status CpuCompiler::RunHloPasses(HloModule* module, bool is_aot_compile) {
// and thread synchronization dependencies which would likely increase
// binary size (and most AOT applications are single-threaded).
// TODO(b/29630486) Support multi-threaded AOT.
- pipeline.AddPass<ParallelTaskAssigner>(max_parallelism,
- ShapeSizeBytesFunction());
+ pipeline.AddPass<ParallelTaskAssigner>(
+ max_parallelism, ShapeSizeBytesFunction(), &target_machine_features);
}
// Copy insertion should be performed immediately before IR emission to avoid
// inserting unnecessary copies (later pass adds an instruction which
@@ -470,7 +474,13 @@ StatusOr<std::unique_ptr<HloModule>> CpuCompiler::RunHloPasses(
VLOG(2) << "Before optimization:";
XLA_VLOG_LINES(2, module->ToString());
- TF_RETURN_IF_ERROR(RunHloPasses(module.get(), /*is_aot_compile=*/false));
+ std::unique_ptr<llvm::TargetMachine> jit_target_machine =
+ SimpleOrcJIT::InferTargetMachineForJIT(
+ CompilerTargetOptions(module->config()),
+ CodeGenOptLevel(module->config()));
+
+ TF_RETURN_IF_ERROR(RunHloPasses(module.get(), /*is_aot_compile=*/false,
+ jit_target_machine.get()));
VLOG(2) << "After optimization:";
XLA_VLOG_LINES(2, module->ToString());
@@ -561,10 +571,11 @@ StatusOr<std::unique_ptr<Executable>> CpuCompiler::RunBackend(
// GetEmbeddedComputations guarantees that a called computation occurs
// before a caller computation.
+ LLVMTargetMachineFeatures target_machine_features(jit->target_machine());
IrEmitter ir_emitter(*module, *assignment, llvm_module.get(),
std::move(instruction_to_profile_idx),
std::move(computation_to_profile_idx),
- jit->target_machine(), jit->external_constant_pool());
+ &target_machine_features, jit->external_constant_pool());
for (auto embedded_computation :
entry_computation->MakeEmbeddedComputationsList()) {
@@ -706,7 +717,8 @@ CpuCompiler::CompileAheadOfTime(std::vector<std::unique_ptr<HloModule>> modules,
VLOG(2) << "Before optimization:";
XLA_VLOG_LINES(2, module->ToString());
- TF_RETURN_IF_ERROR(RunHloPasses(module, /*is_aot_compile=*/true));
+ TF_RETURN_IF_ERROR(
+ RunHloPasses(module, /*is_aot_compile=*/true, target_machine.get()));
VLOG(2) << "After optimization:";
XLA_VLOG_LINES(2, module->ToString());
@@ -746,10 +758,11 @@ CpuCompiler::CompileAheadOfTime(std::vector<std::unique_ptr<HloModule>> modules,
&hlo_profile_index_map, &hlo_profile_printer_data));
}
+ LLVMTargetMachineFeatures target_machine_features(target_machine.get());
IrEmitter ir_emitter(*module, *assignment, &llvm_module,
std::move(instruction_to_profile_idx),
std::move(computation_to_profile_idx),
- target_machine.get(),
+ &target_machine_features,
/*external_constant_pool=*/nullptr);
HloComputation* computation = module->entry_computation();
for (auto embedded_computation :
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.h b/tensorflow/compiler/xla/service/cpu/cpu_compiler.h
index 65b05f04fa..e56f9f0113 100644
--- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.h
+++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.h
@@ -18,6 +18,7 @@ limitations under the License.
#include <memory>
+#include "llvm/Target/TargetMachine.h"
#include "tensorflow/compiler/xla/service/executable.h"
#include "tensorflow/compiler/xla/service/hlo_module.h"
#include "tensorflow/compiler/xla/service/llvm_compiler.h"
@@ -148,7 +149,8 @@ class CpuCompiler : public LLVMCompiler {
// Runs the HLO passes which are necessary for both optimizations and
// correctness.
- Status RunHloPasses(HloModule* module, bool is_aot_compile);
+ Status RunHloPasses(HloModule* module, bool is_aot_compile,
+ llvm::TargetMachine* target_machine);
TF_DISALLOW_COPY_AND_ASSIGN(CpuCompiler);
};
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc
new file mode 100644
index 0000000000..d12fa6bb9a
--- /dev/null
+++ b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc
@@ -0,0 +1,94 @@
+/* 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/cpu/dot_op_emitter.h"
+#include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h"
+#include "tensorflow/compiler/xla/test.h"
+#include "tensorflow/compiler/xla/tools/parser/hlo_parser.h"
+
+namespace xla {
+namespace cpu {
+namespace {
+
+// Test that we don't call into Eigen with tensors too small to be aligned
+// reliably.
+
+class CpuEigenTensorAlignmentTest : public ::testing::Test {};
+
+TEST_F(CpuEigenTensorAlignmentTest, EigenDotAlignment) {
+ string hlo_string = R"(
+HloModule DotOperation
+
+ENTRY DotOperation {
+ arg0 = f32[5,256] parameter(0)
+ arg1 = f32[256,1024] parameter(1)
+ ROOT dot = f32[5,1024] dot(arg0, arg1), lhs_contracting_dims={1}, rhs_contracting_dims={0}
+}
+)";
+
+ TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr<HloModule> module,
+ tools::Parse(hlo_string));
+
+ HloInstruction* dot = module->entry_computation()->root_instruction();
+
+ TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment(
+ [](int64 size) { return 1; });
+
+ EXPECT_FALSE(
+ PotentiallyImplementedAsEigenDot(*dot, target_machine_with_no_alignment));
+
+ TargetMachineFeaturesWithFakeAlignmentLogic
+ target_machine_with_full_alignment([](int64 size) {
+ return TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+
+ EXPECT_TRUE(PotentiallyImplementedAsEigenDot(
+ *dot, target_machine_with_full_alignment));
+}
+
+TEST_F(CpuEigenTensorAlignmentTest, EigenConvAlignment) {
+ string hlo_string = R"(
+HloModule ConvOperation
+
+ENTRY ConvOperation {
+ arg0 = f32[1,2,1] parameter(0)
+ arg1 = f32[1,1,1] parameter(1)
+ ROOT conv = f32[1,2,1] convolution(arg0, arg1), window={size=1}, dim_labels=b0f_0io->b0f
+}
+)";
+
+ TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr<HloModule> module,
+ tools::Parse(hlo_string));
+
+ HloInstruction* conv = module->entry_computation()->root_instruction();
+
+ TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment(
+ [](int64 size) { return 1; });
+
+ EXPECT_FALSE(PotentiallyImplementedAsEigenConvolution(
+ *conv, target_machine_with_no_alignment));
+
+ TargetMachineFeaturesWithFakeAlignmentLogic
+ target_machine_with_full_alignment([](int64 size) {
+ return TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+
+ EXPECT_TRUE(PotentiallyImplementedAsEigenConvolution(
+ *conv, target_machine_with_full_alignment));
+}
+} // namespace
+} // namespace cpu
+} // namespace xla
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc
index 6c642080c3..85c461e6a8 100644
--- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc
+++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc
@@ -100,7 +100,8 @@ Status CpuLayoutAssignment::AddBackendConstraints(
const HloComputation* computation = constraints->computation();
for (auto* instruction : computation->instructions()) {
if (instruction->opcode() == HloOpcode::kConvolution &&
- PotentiallyImplementedAsEigenConvolution(*instruction)) {
+ PotentiallyImplementedAsEigenConvolution(*instruction,
+ target_machine_features_)) {
const HloInstruction* convolution = instruction;
const HloInstruction* lhs_instruction = convolution->operand(0);
const HloInstruction* rhs_instruction = convolution->operand(1);
@@ -126,7 +127,8 @@ Status CpuLayoutAssignment::AddBackendConstraints(
const HloInstruction* op = instruction->operand(*op_idx);
TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
ColMajorShape(op->shape()), instruction, *op_idx));
- } else if (PotentiallyImplementedAsEigenDot(*instruction)) {
+ } else if (PotentiallyImplementedAsEigenDot(*instruction,
+ target_machine_features_)) {
const HloInstruction* dot = instruction;
// In order to implement `dot` with Eigen dot, the layouts of the lhs,
// rhs, and output need to be row-major.
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.h b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.h
index 09adb5cb02..53536a277c 100644
--- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.h
+++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.h
@@ -17,6 +17,7 @@ limitations under the License.
#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_CPU_LAYOUT_ASSIGNMENT_H_
#include "tensorflow/compiler/xla/service/computation_layout.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
#include "tensorflow/compiler/xla/service/layout_assignment.h"
#include "tensorflow/core/lib/core/status.h"
@@ -28,12 +29,16 @@ namespace cpu {
class CpuLayoutAssignment : public LayoutAssignment {
public:
explicit CpuLayoutAssignment(
- const ComputationLayout& entry_computation_layout)
- : LayoutAssignment(entry_computation_layout) {}
+ const ComputationLayout& entry_computation_layout,
+ const TargetMachineFeatures* target_machine_features)
+ : LayoutAssignment(entry_computation_layout),
+ target_machine_features_(*target_machine_features) {}
~CpuLayoutAssignment() override {}
protected:
Status AddBackendConstraints(LayoutConstraints* constraints) override;
+
+ const TargetMachineFeatures& target_machine_features_;
};
} // namespace cpu
diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc
index ba4c5a23d3..f6c93d36f7 100644
--- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc
+++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc
@@ -24,6 +24,7 @@ limitations under the License.
#include "tensorflow/compiler/xla/literal_util.h"
#include "tensorflow/compiler/xla/service/algebraic_simplifier.h"
#include "tensorflow/compiler/xla/service/computation_layout.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h"
#include "tensorflow/compiler/xla/service/hlo_computation.h"
#include "tensorflow/compiler/xla/service/hlo_instruction.h"
#include "tensorflow/compiler/xla/service/hlo_matchers.h"
@@ -49,7 +50,12 @@ class CpuLayoutAssignmentTest : public HloTestBase {
protected:
void AssignLayouts(HloModule* module,
ComputationLayout* entry_computation_layout) {
- cpu::CpuLayoutAssignment layout_assignment(*entry_computation_layout);
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features(
+ [](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+ cpu::CpuLayoutAssignment layout_assignment(*entry_computation_layout,
+ &target_machine_features);
EXPECT_IS_OK(layout_assignment.Run(module).status());
}
};
@@ -311,7 +317,12 @@ static StatusOr<DotOutputFusionLayoutAssignmentResult> RunDotOutputFusion(
result.addend_fusion_param = fusion_instruction->operand(
fused_add->operand(1 - dot_operand_idx_in_add)->parameter_number());
- cpu::CpuLayoutAssignment layout_assignment(computation_layout);
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features(
+ [](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+ cpu::CpuLayoutAssignment layout_assignment(computation_layout,
+ &target_machine_features);
TF_ASSIGN_OR_RETURN(result.layout_assignment_changed_something,
layout_assignment.Run(module));
diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc
index 8db4a0650d..81c0d67cf5 100644
--- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc
+++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc
@@ -23,6 +23,7 @@ limitations under the License.
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h"
#include "tensorflow/compiler/xla/service/cpu/cpu_runtime.h"
+#include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h"
#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
#include "tensorflow/compiler/xla/service/cpu/vector_support_library.h"
#include "tensorflow/compiler/xla/service/hlo_module.h"
@@ -734,7 +735,7 @@ tensorflow::Status DotOpEmitter::Emit() {
CHECK_EQ(addend_array_, nullptr);
- if (PotentiallyImplementedAsEigenDot(dot_)) {
+ if (PotentiallyImplementedAsEigenDot(dot_, target_machine_features_)) {
return EmitCallToRuntime();
}
@@ -1058,19 +1059,39 @@ static bool IsRank2WithNoPadding(const Shape& shape) {
// In a gemm operation where output = lhs * rhs, check whether the given shapes
// are valid for the operation.
-static bool AreValidGemmShapes(const Shape& lhs_shape, const Shape& rhs_shape,
- const Shape& output_shape) {
+static bool AreValidGemmShapes(
+ const Shape& lhs_shape, const Shape& rhs_shape, const Shape& output_shape,
+ const TargetMachineFeatures& target_machine_features) {
// The inputs and the output must
// 1) be matrices with no padding, and
// 2) have an allowed element type.
PrimitiveType output_primitive_type = output_shape.element_type();
- return (output_primitive_type == F64 || output_primitive_type == F32 ||
- output_primitive_type == F16) &&
- IsRank2WithNoPadding(lhs_shape) && IsRank2WithNoPadding(rhs_shape) &&
- IsRank2WithNoPadding(output_shape);
+ if (!(output_primitive_type == F64 || output_primitive_type == F32 ||
+ output_primitive_type == F16)) {
+ return false;
+ }
+
+ if (!(IsRank2WithNoPadding(lhs_shape) && IsRank2WithNoPadding(rhs_shape) &&
+ IsRank2WithNoPadding(output_shape))) {
+ return false;
+ }
+
+ auto is_aligned = [&](const Shape& shape) {
+ return GetMinimumAlignmentForArray(shape, target_machine_features) >=
+ TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ };
+
+ if (!is_aligned(lhs_shape) || !is_aligned(rhs_shape) ||
+ !is_aligned(output_shape)) {
+ return false;
+ }
+
+ return true;
}
-bool PotentiallyImplementedAsEigenDot(const HloInstruction& hlo) {
+bool PotentiallyImplementedAsEigenDot(
+ const HloInstruction& hlo,
+ const TargetMachineFeatures& target_machine_features) {
// For certain types of Dot, we can call Eigen
if (hlo.opcode() == HloOpcode::kDot) {
const Shape& lhs_shape = hlo.operand(0)->shape();
@@ -1087,7 +1108,8 @@ bool PotentiallyImplementedAsEigenDot(const HloInstruction& hlo) {
// If gemm can accept the operand shapes, use it rather than a custom
// kernel.
- if (AreValidGemmShapes(lhs_shape, rhs_shape, hlo.shape())) {
+ if (AreValidGemmShapes(lhs_shape, rhs_shape, hlo.shape(),
+ target_machine_features)) {
const DotDimensionNumbers& dim_numbers = hlo.dot_dimension_numbers();
// The size of the reduction dimension should match. The shape inference
// guarantees this invariant, so the check here is for programming
diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h
index a20bf2f9db..e5ede066f2 100644
--- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h
+++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h
@@ -31,7 +31,9 @@ limitations under the License.
namespace xla {
namespace cpu {
-bool PotentiallyImplementedAsEigenDot(const HloInstruction& hlo);
+bool PotentiallyImplementedAsEigenDot(
+ const HloInstruction& hlo,
+ const TargetMachineFeatures& target_machine_features);
// Returns the index for an operand to `hlo` that should ideally be column
// major. Returns nullopt if there is no such operand or if `hlo` is not a dot
diff --git a/tensorflow/compiler/xla/service/cpu/ir_emission_utils.cc b/tensorflow/compiler/xla/service/cpu/ir_emission_utils.cc
index f209a69e3c..b560b7531c 100644
--- a/tensorflow/compiler/xla/service/cpu/ir_emission_utils.cc
+++ b/tensorflow/compiler/xla/service/cpu/ir_emission_utils.cc
@@ -24,8 +24,25 @@ limitations under the License.
namespace xla {
namespace cpu {
+int64 GetMinimumAlignmentForArray(
+ const Shape& shape, const TargetMachineFeatures& target_machine_features) {
+ CHECK(ShapeUtil::IsArray(shape));
+ CHECK(!LayoutUtil::HasLayout(shape) || LayoutUtil::IsDense(shape.layout()));
+
+ // We don't require a layout to be set on `shape`. This only works on CPU
+ // because we don't pad our tensors or otherwise have complicated data tiling
+ // schemes.
+
+ int64 allocation_size_bytes =
+ ShapeUtil::ElementsIn(shape) *
+ ShapeUtil::ByteSizeOfPrimitiveType(shape.element_type());
+ return target_machine_features.minimum_alignment_for_allocation(
+ allocation_size_bytes);
+}
+
bool PotentiallyImplementedAsEigenConvolution(
- const HloInstruction& convolution) {
+ const HloInstruction& convolution,
+ const TargetMachineFeatures& target_machine_features) {
// The following conditions are necessary (but not sufficient) for
// implementing `convolution` with Eigen convolution:
// - the input and kernel have a non-zero number of elements.
@@ -35,6 +52,18 @@ bool PotentiallyImplementedAsEigenConvolution(
// To be sufficient, certain layout constraints need to be satisfied as well.
const Shape& input_shape = convolution.operand(0)->shape();
const Shape& kernel_shape = convolution.operand(1)->shape();
+ const Shape& output_shape = convolution.shape();
+
+ auto is_aligned = [&](const Shape& shape) {
+ return GetMinimumAlignmentForArray(shape, target_machine_features) >=
+ TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ };
+
+ if (!is_aligned(input_shape) || !is_aligned(kernel_shape) ||
+ !is_aligned(output_shape)) {
+ return false;
+ }
+
if (ShapeUtil::HasZeroElements(input_shape) ||
ShapeUtil::HasZeroElements(kernel_shape)) {
return false;
@@ -71,7 +100,6 @@ bool PotentiallyImplementedAsEigenConvolution(
}
}
- const Shape& output_shape = convolution.shape();
return dnums.input_batch_dimension() == 0 &&
dnums.input_feature_dimension() == input_shape.dimensions_size() - 1 &&
dnums.output_batch_dimension() == 0 &&
diff --git a/tensorflow/compiler/xla/service/cpu/ir_emission_utils.h b/tensorflow/compiler/xla/service/cpu/ir_emission_utils.h
index 34b2003916..68fbc7caaa 100644
--- a/tensorflow/compiler/xla/service/cpu/ir_emission_utils.h
+++ b/tensorflow/compiler/xla/service/cpu/ir_emission_utils.h
@@ -17,13 +17,20 @@ limitations under the License.
#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_IR_EMISSION_UTILS_H_
#include "llvm/IR/Value.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
#include "tensorflow/compiler/xla/service/hlo_instruction.h"
namespace xla {
namespace cpu {
bool PotentiallyImplementedAsEigenConvolution(
- const HloInstruction& convolution);
+ const HloInstruction& convolution,
+ const TargetMachineFeatures& target_machine_features);
+
+// Computes the minimum alignment guaranteed for a tensor of shape `shape` on
+// the target machine.
+int64 GetMinimumAlignmentForArray(
+ const Shape& shape, const TargetMachineFeatures& target_machine_features);
// Dynamic loop bounds are specified as an array of dimension index
// [start, limit) pairs of ir values (one for each partitioned outer dimension).
diff --git a/tensorflow/compiler/xla/service/cpu/ir_emission_utils_test.cc b/tensorflow/compiler/xla/service/cpu/ir_emission_utils_test.cc
index 215f48c4cc..abb2471e6a 100644
--- a/tensorflow/compiler/xla/service/cpu/ir_emission_utils_test.cc
+++ b/tensorflow/compiler/xla/service/cpu/ir_emission_utils_test.cc
@@ -15,6 +15,7 @@ limitations under the License.
#include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h"
#include "tensorflow/compiler/xla/test.h"
#include "tensorflow/compiler/xla/tools/parser/hlo_parser.h"
@@ -39,7 +40,12 @@ ENTRY Conv {
HloComputation* entry_computation = module->entry_computation();
HloInstruction* conv_instr = entry_computation->root_instruction();
- EXPECT_FALSE(cpu::PotentiallyImplementedAsEigenConvolution(*conv_instr));
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features(
+ [](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ });
+ EXPECT_FALSE(cpu::PotentiallyImplementedAsEigenConvolution(
+ *conv_instr, target_machine_features));
}
} // namespace
diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc
index 55e5aa5063..44cf9ac110 100644
--- a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc
+++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc
@@ -83,7 +83,7 @@ IrEmitter::IrEmitter(
llvm::Module* llvm_module,
std::unordered_map<const HloInstruction*, int64> instruction_to_profile_idx,
std::unordered_map<const HloComputation*, int64> computation_to_profile_idx,
- llvm::TargetMachine* target_machine,
+ const TargetMachineFeatures* target_machine_features,
ExternalConstantPool* external_constant_pool)
: assignment_(assignment),
module_(llvm_module),
@@ -94,7 +94,7 @@ IrEmitter::IrEmitter(
alias_analysis_(hlo_module, assignment, &llvm_module->getContext()),
hlo_module_config_(hlo_module.config()),
is_top_level_computation_(false),
- target_machine_features_(target_machine),
+ target_machine_features_(*target_machine_features),
external_constant_pool_(external_constant_pool) {
ir_builder_.setFastMathFlags(llvm_ir::GetFastMathFlags(
/*fast_math_enabled=*/hlo_module_config_.debug_options()
@@ -227,32 +227,6 @@ Status IrEmitter::HandleCopy(HloInstruction* copy) {
}
}
-// Calculate the alignment of a buffer with a particular size.
-int IrEmitter::MinimumAlignmentForBufferSize(int64 buffer_size) {
- // GLibc returns a pointer with alignment 8 on 32-bit platforms and 16 on
- // 64-bit platforms. TCMalloc returns a pointer with alignment 8 for
- // allocations smaller than kMallocAlignmentThreshold bytes and at least
- // alignment 16 for allocations greater than or equal to
- // kMallocAlignmentThreshold bytes. N.B. We could improve on this lower bound
- // by explicitly allocating the memory with posix_memalign. This is
- // complicated by our desire to allow parameter buffers created by clients to
- // be consumed directly by the JIT.
- if (buffer_size == 0) {
- // No need to align empty buffers.
- return 1;
- }
-
- const int64 kMallocAlignmentThreshold = 512;
-
- int pointer_size = module_->getDataLayout().getPointerSize();
- int buffer_alignment = buffer_size >= kMallocAlignmentThreshold
- ? 2 * pointer_size
- : pointer_size;
- DCHECK_GT(buffer_alignment, 0);
-
- return buffer_alignment;
-}
-
// Calculate the alignment of a buffer allocated for a given primitive type.
int IrEmitter::MinimumAlignmentForPrimitiveType(PrimitiveType primitive_type) {
int64 byte_size = ShapeUtil::ByteSizeOfPrimitiveType(primitive_type);
@@ -277,7 +251,7 @@ int IrEmitter::MinimumAlignmentForShape(const Shape& shape) {
DCHECK_GE(buffer_size, 0);
DCHECK_LE(buffer_size, SIZE_MAX);
- return MinimumAlignmentForBufferSize(buffer_size);
+ return target_machine_features_.minimum_alignment_for_allocation(buffer_size);
}
void IrEmitter::AttachAlignmentMetadataForLoad(llvm::LoadInst* load,
@@ -290,7 +264,8 @@ void IrEmitter::AttachAlignmentMetadataForLoad(llvm::LoadInst* load,
void IrEmitter::AttachAlignmentMetadataForLoad(llvm::LoadInst* load,
int64 buffer_size) {
- int alignment = MinimumAlignmentForBufferSize(buffer_size);
+ int alignment =
+ target_machine_features_.minimum_alignment_for_allocation(buffer_size);
if (alignment > 1) {
llvm_ir::SetAlignmentMetadataForLoad(load, alignment);
}
@@ -861,7 +836,8 @@ Status IrEmitter::HandleConvolution(HloInstruction* convolution) {
// TODO(tonywy): Add PotentiallyImplementedAsMKLCovolution to support
// different data layouts.
- if (PotentiallyImplementedAsEigenConvolution(*convolution)) {
+ if (PotentiallyImplementedAsEigenConvolution(*convolution,
+ target_machine_features_)) {
const Shape& lhs_shape = lhs->shape();
const Shape& rhs_shape = rhs->shape();
const Shape& convolution_shape = convolution->shape();
@@ -1027,12 +1003,14 @@ Status IrEmitter::HandleConvolution(HloInstruction* convolution) {
// We will accumulate the products into this sum to calculate
// the output entry at the given index.
PrimitiveType lhs_element_type = lhs->shape().element_type();
+ llvm::Type* lhs_llvm_type =
+ llvm_ir::PrimitiveTypeToIrType(lhs_element_type, module_);
llvm::Value* sum_address = llvm_ir::EmitAllocaAtFunctionEntry(
- llvm_ir::PrimitiveTypeToIrType(lhs_element_type, module_),
- "convolution_sum_address", &ir_builder_,
+ lhs_llvm_type, "convolution_sum_address", &ir_builder_,
MinimumAlignmentForPrimitiveType(lhs_element_type));
- ir_builder_.CreateStore(
- llvm::ConstantFP::get(ir_builder_.getFloatTy(), 0.0), sum_address);
+ llvm::Value* constant_zero =
+ llvm::Constant::getNullValue(lhs_llvm_type);
+ ir_builder_.CreateStore(constant_zero, sum_address);
llvm_ir::ForLoopNest loops(IrName(convolution, "inner"), &ir_builder_);
std::vector<llvm::Value*> kernel_spatial(num_spatial_dims);
diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.h b/tensorflow/compiler/xla/service/cpu/ir_emitter.h
index 5a04076080..f49cfc1dc3 100644
--- a/tensorflow/compiler/xla/service/cpu/ir_emitter.h
+++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.h
@@ -76,7 +76,7 @@ class IrEmitter : public DfsHloVisitorWithDefault {
instruction_to_profile_idx,
std::unordered_map<const HloComputation*, int64>
computation_to_profile_idx,
- llvm::TargetMachine* target_machine,
+ const TargetMachineFeatures* target_machine,
ExternalConstantPool* external_constant_pool);
~IrEmitter() override;
@@ -514,9 +514,6 @@ class IrEmitter : public DfsHloVisitorWithDefault {
// Calculate the alignment of a buffer allocated for a given primitive type.
int MinimumAlignmentForPrimitiveType(PrimitiveType primitive_type);
- // Calculate the alignment of a buffer with a particular size.
- int MinimumAlignmentForBufferSize(int64 buffer_size);
-
// Returns the number of bytes within the shape.
int64 ByteSizeOf(const Shape& shape) const;
@@ -536,7 +533,7 @@ class IrEmitter : public DfsHloVisitorWithDefault {
bool is_top_level_computation_;
- TargetMachineFeatures target_machine_features_;
+ const TargetMachineFeatures& target_machine_features_;
int64 external_global_constant_counter_ = 0;
ExternalConstantPool* external_constant_pool_;
diff --git a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc
index 47e8405ff2..63d0f7b95c 100644
--- a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc
+++ b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc
@@ -104,7 +104,9 @@ class DefaultCostModel : public ParallelCostModel {
ParallelTaskAssignment::ParallelTaskAssignment(
const int64 max_parallelism,
- const HloCostAnalysis::ShapeSizeFunction& shape_size, HloModule* module) {
+ const HloCostAnalysis::ShapeSizeFunction& shape_size, HloModule* module,
+ const TargetMachineFeatures* target_machine_features)
+ : target_machine_features_(*target_machine_features) {
VLOG(1) << "ParallelTaskAssignment max_parallelism: " << max_parallelism;
// Run cost analysis on 'module'.
auto cost_analysis = MakeUnique<HloCostAnalysis>(shape_size);
@@ -139,8 +141,10 @@ int64 ParallelTaskAssignment::GetTargetParallelTaskCount(
opcode == HloOpcode::kFft || opcode == HloOpcode::kInfeed ||
opcode == HloOpcode::kOutfeed || opcode == HloOpcode::kRng ||
(opcode == HloOpcode::kConvolution &&
- PotentiallyImplementedAsEigenConvolution(*instruction)) ||
- PotentiallyImplementedAsEigenDot(*instruction) ||
+ PotentiallyImplementedAsEigenConvolution(*instruction,
+ target_machine_features_)) ||
+ PotentiallyImplementedAsEigenDot(*instruction,
+ target_machine_features_) ||
(opcode == HloOpcode::kFusion &&
instruction->fusion_kind() != HloInstruction::FusionKind::kLoop) ||
ShapeUtil::IsTuple(instruction->shape())) {
@@ -231,7 +235,8 @@ bool ParallelTaskAssigner::AssignParallelTasksHelper(
void ParallelTaskAssigner::ComputeTargetParallelTasks(
HloModule* module, HloToParallelTasks* hlo_to_parallel_tasks) {
ParallelTaskAssignment parallel_task_assignment(max_parallelism_,
- shape_size_function_, module);
+ shape_size_function_, module,
+ &target_machine_features_);
// Compute parallel task counts for all instructions in 'module'.
for (auto* computation : module->computations()) {
diff --git a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h
index 7140dabe51..8becc8fa23 100644
--- a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h
+++ b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h
@@ -16,6 +16,7 @@ limitations under the License.
#ifndef TENSORFLOW_COMPILER_XLA_SERVICE_CPU_PARALLEL_TASK_ASSIGNMENT_H_
#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_PARALLEL_TASK_ASSIGNMENT_H_
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
#include "tensorflow/compiler/xla/service/hlo_cost_analysis.h"
#include "tensorflow/compiler/xla/service/hlo_module.h"
#include "tensorflow/compiler/xla/service/hlo_pass_interface.h"
@@ -39,7 +40,8 @@ class ParallelTaskAssignment {
// 'module': the containing HloModule.
ParallelTaskAssignment(const int64 max_parallelism,
const HloCostAnalysis::ShapeSizeFunction& shape_size,
- HloModule* module);
+ HloModule* module,
+ const TargetMachineFeatures* target_machine_features);
~ParallelTaskAssignment() {}
// Computes and returns the target parallel task count for 'instruction'.
@@ -47,6 +49,7 @@ class ParallelTaskAssignment {
private:
std::unique_ptr<ParallelCostModel> cost_model_;
+ const TargetMachineFeatures& target_machine_features_;
};
// ParallelTaskAssigner computes target parallel task counts for all HLOs
@@ -63,8 +66,11 @@ class ParallelTaskAssigner : public HloPassInterface {
// 'shape_size': shape size function used by HloCostAnalysis during parallel
// task assignment.
ParallelTaskAssigner(const int64 max_parallelism,
- const HloCostAnalysis::ShapeSizeFunction& shape_size)
- : max_parallelism_(max_parallelism), shape_size_function_(shape_size) {}
+ const HloCostAnalysis::ShapeSizeFunction& shape_size,
+ const TargetMachineFeatures* target_machine_features)
+ : max_parallelism_(max_parallelism),
+ shape_size_function_(shape_size),
+ target_machine_features_(*target_machine_features) {}
~ParallelTaskAssigner() override {}
tensorflow::StringPiece name() const override {
@@ -94,6 +100,7 @@ class ParallelTaskAssigner : public HloPassInterface {
int64 max_parallelism_;
HloCostAnalysis::ShapeSizeFunction shape_size_function_;
+ const TargetMachineFeatures& target_machine_features_;
};
} // namespace cpu
diff --git a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment_test.cc b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment_test.cc
index 13eb75a572..fc2efbaf9a 100644
--- a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment_test.cc
+++ b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment_test.cc
@@ -15,6 +15,7 @@ limitations under the License.
#include "tensorflow/compiler/xla/service/cpu/parallel_task_assignment.h"
#include "tensorflow/compiler/xla/service/cpu/cpu_executable.h"
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h"
#include "tensorflow/compiler/xla/test.h"
#include "tensorflow/compiler/xla/tests/hlo_verified_test_base.h"
#include "tensorflow/core/lib/core/status_test_util.h"
@@ -31,6 +32,19 @@ class ParallelTaskAssignmentTest : public HloVerifiedTestBase {
// Use any value larger than 2 since we only test whether a module is
// parallelized or not
const int max_parallelism_ = 10;
+
+ cpu::TargetMachineFeaturesWithFakeAlignmentLogic target_machine_features_;
+
+ ParallelTaskAssignmentTest()
+ : target_machine_features_([](int64 shape_size) {
+ return cpu::TargetMachineFeatures::kEigenExpectedTensorAlignment;
+ }) {}
+
+ StatusOr<bool> RunParallelTaskAssigner(HloModule* module) {
+ return cpu::ParallelTaskAssigner(max_parallelism_, shape_size_func_,
+ &target_machine_features_)
+ .Run(module);
+ }
};
TEST_F(ParallelTaskAssignmentTest, DotOperationNotParallelized) {
@@ -45,9 +59,7 @@ TEST_F(ParallelTaskAssignmentTest, DotOperationNotParallelized) {
)";
ParseAndVerifyModule(hlo_string);
- TF_ASSERT_OK_AND_ASSIGN(bool changed, cpu::ParallelTaskAssigner(
- max_parallelism_, shape_size_func_)
- .Run(&module()));
+ TF_ASSERT_OK_AND_ASSIGN(bool changed, RunParallelTaskAssigner(&module()));
EXPECT_FALSE(changed);
}
@@ -74,9 +86,7 @@ TEST_F(ParallelTaskAssignmentTest,
)";
ParseAndVerifyModule(hlo_string);
- TF_ASSERT_OK_AND_ASSIGN(bool changed, cpu::ParallelTaskAssigner(
- max_parallelism_, shape_size_func_)
- .Run(&module()));
+ TF_ASSERT_OK_AND_ASSIGN(bool changed, RunParallelTaskAssigner(&module()));
EXPECT_FALSE(changed);
}
@@ -92,9 +102,7 @@ TEST_F(ParallelTaskAssignmentTest, RngOperationNotParallelized) {
)";
ParseAndVerifyModule(hlo_string);
- TF_ASSERT_OK_AND_ASSIGN(bool changed, cpu::ParallelTaskAssigner(
- max_parallelism_, shape_size_func_)
- .Run(&module()));
+ TF_ASSERT_OK_AND_ASSIGN(bool changed, RunParallelTaskAssigner(&module()));
EXPECT_FALSE(changed);
}
@@ -108,9 +116,7 @@ TEST_F(ParallelTaskAssignmentTest, InfeedOutfeedOperationNotParallelized) {
)";
ParseAndVerifyModule(hlo_string);
- TF_ASSERT_OK_AND_ASSIGN(bool changed, cpu::ParallelTaskAssigner(
- max_parallelism_, shape_size_func_)
- .Run(&module()));
+ TF_ASSERT_OK_AND_ASSIGN(bool changed, RunParallelTaskAssigner(&module()));
EXPECT_FALSE(changed);
}
diff --git a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc
index ff6f0a9d4e..62c97e5641 100644
--- a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc
+++ b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc
@@ -73,20 +73,29 @@ llvm::StringRef GetHostCpuName() {
}
} // namespace
+/*static*/ std::unique_ptr<llvm::TargetMachine>
+SimpleOrcJIT::InferTargetMachineForJIT(
+ const llvm::TargetOptions& target_options,
+ llvm::CodeGenOpt::Level opt_level) {
+ std::unique_ptr<llvm::TargetMachine> target_machine(
+ llvm::EngineBuilder()
+ .setTargetOptions(target_options)
+ .setOptLevel(opt_level)
+ .selectTarget(
+ /*TargetTriple=*/llvm::Triple(), /*MArch=*/"",
+ /*MCPU=*/GetHostCpuName(),
+ /*MAttrs=*/DetectMachineAttributes()));
+ CHECK(target_machine != nullptr);
+ return target_machine;
+}
+
SimpleOrcJIT::SimpleOrcJIT(const llvm::TargetOptions& target_options,
llvm::CodeGenOpt::Level opt_level,
bool optimize_for_size, bool enable_fast_math,
bool disable_expensive_passes,
LLVMCompiler::ModuleHook pre_optimization_hook,
LLVMCompiler::ModuleHook post_optimization_hook)
- : target_machine_(
- CHECK_NOTNULL(llvm::EngineBuilder()
- .setTargetOptions(target_options)
- .setOptLevel(opt_level)
- .selectTarget(
- /*TargetTriple=*/llvm::Triple(), /*MArch=*/"",
- /*MCPU=*/GetHostCpuName(),
- /*MAttrs=*/DetectMachineAttributes()))),
+ : target_machine_(InferTargetMachineForJIT(target_options, opt_level)),
disassembler_(*target_machine_),
data_layout_(target_machine_->createDataLayout()),
symbol_resolver_(llvm::orc::createLegacyLookupResolver(
diff --git a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.h b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.h
index f4260a95bc..1851a3ee0b 100644
--- a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.h
+++ b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.h
@@ -95,6 +95,12 @@ class SimpleOrcJIT {
return &external_constant_pool_;
}
+ // Creates an llvm::TargetMachine suitable for JITting code that will run on
+ // the current machine.
+ static std::unique_ptr<llvm::TargetMachine> InferTargetMachineForJIT(
+ const llvm::TargetOptions& target_options,
+ llvm::CodeGenOpt::Level opt_level);
+
private:
llvm::JITSymbol ResolveRuntimeSymbol(const std::string& name);
diff --git a/tensorflow/compiler/xla/service/cpu/target_machine_features.cc b/tensorflow/compiler/xla/service/cpu/target_machine_features.cc
index eeb049737d..a0cd8ee2d2 100644
--- a/tensorflow/compiler/xla/service/cpu/target_machine_features.cc
+++ b/tensorflow/compiler/xla/service/cpu/target_machine_features.cc
@@ -18,7 +18,7 @@ limitations under the License.
namespace xla {
namespace cpu {
-llvm::TargetTransformInfo* TargetMachineFeatures::GetTargetTransformInfoFor(
+llvm::TargetTransformInfo* LLVMTargetMachineFeatures::GetTargetTransformInfoFor(
const llvm::Function& function) const {
auto it = target_transform_info_cache_.find(&function);
if (it == target_transform_info_cache_.end()) {
@@ -31,5 +31,30 @@ llvm::TargetTransformInfo* TargetMachineFeatures::GetTargetTransformInfoFor(
return &it->second;
}
+int64 LLVMTargetMachineFeatures::minimum_alignment_for_allocation(
+ int64 size_bytes) const {
+ // GLibc malloc returns a pointer with alignment 8 on 32-bit platforms and 16
+ // on 64-bit platforms. TCMalloc returns a pointer with alignment 8 for
+ // allocations smaller than kMallocAlignmentThreshold bytes and at least
+ // alignment 16 for allocations greater than or equal to
+ // kMallocAlignmentThreshold bytes. N.B. We could improve on this lower bound
+ // by explicitly allocating the memory with posix_memalign. This is
+ // complicated by our desire to allow parameter buffers created by clients to
+ // be consumed directly by the JIT.
+ if (size_bytes == 0) {
+ // No need to align empty buffers.
+ return 1;
+ }
+
+ const int64 kMallocAlignmentThreshold = 512;
+
+ int pointer_size = target_machine_->getPointerSize(0);
+ int buffer_alignment =
+ size_bytes >= kMallocAlignmentThreshold ? 2 * pointer_size : pointer_size;
+ DCHECK_GT(buffer_alignment, 0);
+
+ return buffer_alignment;
+}
+
} // namespace cpu
} // namespace xla
diff --git a/tensorflow/compiler/xla/service/cpu/target_machine_features.h b/tensorflow/compiler/xla/service/cpu/target_machine_features.h
index 703942615e..8b00ae9e47 100644
--- a/tensorflow/compiler/xla/service/cpu/target_machine_features.h
+++ b/tensorflow/compiler/xla/service/cpu/target_machine_features.h
@@ -24,43 +24,68 @@ limitations under the License.
namespace xla {
namespace cpu {
-// Wraps an llvm::TargetMachine and parses out some information that feeds into
-// LLVM IR code generation decisions.
+// Abstract interface for classes providing information about the target we're
+// compiling for.
class TargetMachineFeatures {
public:
static constexpr int kX86AvxVectorByteSize = 32;
- TargetMachineFeatures(llvm::TargetMachine* target_machine)
- : target_machine_(target_machine) {}
+ // Input and output tensor buffers must be aligned to this many bytes if we
+ // want to call an Eigen backed GEMM or Convolution.
+ static constexpr int kEigenExpectedTensorAlignment = 16;
// Return the vectorization factor, which is the number of bytes of data
// explicitly vectorized routines will try to process at once.
- int vectorization_factor_in_bytes() const {
- // Ideally this should be a function of the cache line size (which we can
- // get from llvm::TargetTransformInfo::getCacheLineSize) of the target
- // machine. Guess a value of 128 bytes for now.
- return 128;
- }
+ virtual int vectorization_factor_in_bytes() const = 0;
// Return the size of the largest vector size in bytes. We need to pass in
// "function" since llvm functions can contain annotations for specializing
// them to specific micro-architectures (though currently XLA does not use
// this functionality).
- int vector_register_byte_size(const llvm::Function& function) const {
- llvm::TargetTransformInfo* tti = GetTargetTransformInfoFor(function);
- return tti->getRegisterBitWidth(/*Vector=*/true) / 8;
- }
+ virtual int vector_register_byte_size(
+ const llvm::Function& function) const = 0;
// Return the number of elements of type `type` that can fit into the largest
// vector register available. We need to pass in "function" since llvm
// functions can contain annotations for specializing them to specific
// micro-architectures (though currently XLA does not use this functionality).
+ virtual int vector_register_num_elements(const llvm::Function& function,
+ PrimitiveType type) const = 0;
+
+ // Returns the minimum alignment for a buffer of size size_bytes.
+ virtual int64 minimum_alignment_for_allocation(int64 size_bytes) const = 0;
+
+ virtual ~TargetMachineFeatures() = default;
+};
+
+// Implements the TargetMachineFeatures interface using an llvm::TargetMachine.
+class LLVMTargetMachineFeatures : public TargetMachineFeatures {
+ public:
+ static constexpr int kX86AvxVectorByteSize = 32;
+
+ LLVMTargetMachineFeatures(llvm::TargetMachine* target_machine)
+ : target_machine_(target_machine) {}
+
+ int vectorization_factor_in_bytes() const override {
+ // Ideally this should be a function of the cache line size (which we can
+ // get from llvm::TargetTransformInfo::getCacheLineSize) of the target
+ // machine. Guess a value of 128 bytes for now.
+ return 128;
+ }
+
+ int vector_register_byte_size(const llvm::Function& function) const override {
+ llvm::TargetTransformInfo* tti = GetTargetTransformInfoFor(function);
+ return tti->getRegisterBitWidth(/*Vector=*/true) / 8;
+ }
+
int vector_register_num_elements(const llvm::Function& function,
- PrimitiveType type) const {
+ PrimitiveType type) const override {
return vector_register_byte_size(function) /
(primitive_util::BitWidth(type) / 8);
}
+ int64 minimum_alignment_for_allocation(int64 size_bytes) const override;
+
private:
llvm::TargetTransformInfo* GetTargetTransformInfoFor(
const llvm::Function& function) const;
diff --git a/tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h b/tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h
new file mode 100644
index 0000000000..ffc6927cbe
--- /dev/null
+++ b/tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h
@@ -0,0 +1,57 @@
+/* Copyright 2018 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.
+==============================================================================*/
+
+#ifndef TENSORFLOW_COMPILER_XLA_SERVICE_CPU_TARGET_MACHINE_FEATURES_FAKE_H_
+#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_TARGET_MACHINE_FEATURES_FAKE_H_
+
+#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h"
+
+namespace xla {
+namespace cpu {
+// Delegates calls to minimum_alignment_for_allocation to a user provided
+// std::function, crashes on all other methods.
+//
+// Primarily useful for testing.
+class TargetMachineFeaturesWithFakeAlignmentLogic
+ : public TargetMachineFeatures {
+ public:
+ explicit TargetMachineFeaturesWithFakeAlignmentLogic(
+ std::function<int64(int64)> fake_alignment_logic)
+ : fake_alignment_logic_(std::move(fake_alignment_logic)) {}
+
+ int vectorization_factor_in_bytes() const override {
+ LOG(FATAL) << "Unexpected call to " << __func__;
+ }
+
+ int vector_register_byte_size(const llvm::Function& function) const override {
+ LOG(FATAL) << "Unexpected call to " << __func__;
+ }
+
+ int vector_register_num_elements(const llvm::Function& function,
+ PrimitiveType type) const override {
+ LOG(FATAL) << "Unexpected call to " << __func__;
+ }
+
+ int64 minimum_alignment_for_allocation(int64 size_bytes) const override {
+ return fake_alignment_logic_(size_bytes);
+ }
+
+ private:
+ std::function<int64(int64)> fake_alignment_logic_;
+};
+} // namespace cpu
+} // namespace xla
+
+#endif // TENSORFLOW_COMPILER_XLA_SERVICE_CPU_TARGET_MACHINE_FEATURES_FAKE_H_