1 /* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include <memory>
17 #include <utility>
18
19 #include "llvm/IR/Module.h"
20 #include "tensorflow/compiler/xla/literal.h"
21 #include "tensorflow/compiler/xla/service/buffer_assignment.h"
22 #include "tensorflow/compiler/xla/service/cpu/tests/cpu_codegen_test.h"
23 #include "tensorflow/compiler/xla/service/hlo_computation.h"
24 #include "tensorflow/compiler/xla/service/hlo_instruction.h"
25 #include "tensorflow/compiler/xla/service/hlo_module.h"
26 #include "tensorflow/compiler/xla/service/llvm_ir/alias_analysis.h"
27 #include "tensorflow/compiler/xla/service/llvm_ir/llvm_util.h"
28 #include "tensorflow/compiler/xla/shape_util.h"
29 #include "tensorflow/compiler/xla/tests/filecheck.h"
30 #include "tensorflow/compiler/xla/xla_data.pb.h"
31 #include "tensorflow/core/platform/test.h"
32
33 namespace xla {
34 namespace cpu {
35
36 class CpuNoAliasTest : public CpuCodegenTest {};
37
38 // Creates a simple HLO ir_module (runs concat(concat(x, y), x)), and then
39 // inspects the aliasing information for loads to its buffers.
TEST_F(CpuNoAliasTest,Concat)40 TEST_F(CpuNoAliasTest, Concat) {
41 HloComputation::Builder builder(TestName());
42
43 Literal literal = LiteralUtil::CreateR2<float>({{1.0, 2.0}, {3.0, 4.0}});
44 auto param_shape = ShapeUtil::MakeShape(F32, {2, 2});
45 HloInstruction* param_x = builder.AddInstruction(
46 HloInstruction::CreateParameter(0, param_shape, "x"));
47 HloInstruction* param_y = builder.AddInstruction(
48 HloInstruction::CreateParameter(1, param_shape, "y"));
49 HloInstruction* concat1 =
50 builder.AddInstruction(HloInstruction::CreateConcatenate(
51 ShapeUtil::MakeShape(F32, {2, 4}), {param_x, param_y}, 1));
52 HloInstruction* concat2 =
53 builder.AddInstruction(HloInstruction::CreateConcatenate(
54 ShapeUtil::MakeShape(F32, {2, 6}), {concat1, param_x}, 1));
55 HloInstruction* add = builder.AddInstruction(HloInstruction::CreateBinary(
56 ShapeUtil::MakeShape(F32, {2, 6}), HloOpcode::kAdd, concat2, concat2));
57
58 std::unique_ptr<HloComputation> computation = builder.Build();
59
60 auto hlo_module = CreateNewVerifiedModule();
61 hlo_module->AddEntryComputation(std::move(computation));
62
63 // Now that we have an HLO module, build an llvm_ir::AliasAnalysis for it.
64 auto status_or_buffer_assn = BufferAssigner::Run(
65 hlo_module.get(),
66 std::make_unique<DependencyHloOrdering>(hlo_module.get()),
67 backend().compiler()->BufferSizeBytesFunction(),
68 [](LogicalBuffer::Color) { return /*alignment=*/1; });
69 ASSERT_EQ(status_or_buffer_assn.status(), OkStatus());
70
71 llvm::LLVMContext context;
72 llvm_ir::AliasAnalysis aa(*hlo_module, *status_or_buffer_assn.ValueOrDie(),
73 &context);
74
75 // Construct an LLVM module containing loads that we annotate as being from
76 // the buffers in the HLO module. We'll inspect these loads to ensure that
77 // they have the expected alias information.
78 llvm::Module ir_module("test", context);
79 llvm::Function* func = llvm::dyn_cast<llvm::Function>(
80 ir_module.getOrInsertFunction("test_fn", llvm::Type::getVoidTy(context))
81 .getCallee());
82 llvm::BasicBlock* bb = llvm::BasicBlock::Create(context, "body", func);
83 llvm::IRBuilder<> b(bb);
84 auto* zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 0);
85
86 llvm::ArrayType* array2d_type = llvm::ArrayType::get(
87 llvm::ArrayType::get(llvm::Type::getFloatTy(context), 100), 100);
88
89 {
90 auto param_x_val = llvm::cast<llvm::GlobalVariable>(
91 ir_module.getOrInsertGlobal("param_x", array2d_type));
92 llvm_ir::IrArray param_x_array(param_x_val, param_x_val->getValueType(),
93 param_shape);
94 aa.AddAliasingInformationToIrArray(*param_x, ¶m_x_array);
95 llvm_ir::IrArray::Index zero_2d({zero, zero}, param_shape, zero->getType());
96 param_x_array.EmitReadArrayElement(zero_2d, &b)
97 ->setName("read_param_x_array");
98 }
99
100 {
101 auto concat1_val = llvm::cast<llvm::GlobalVariable>(
102 ir_module.getOrInsertGlobal("concat1", array2d_type));
103 auto shape = ShapeUtil::MakeShape(F32, {2, 4});
104 llvm_ir::IrArray concat1_array(concat1_val, concat1_val->getValueType(),
105 shape);
106 aa.AddAliasingInformationToIrArray(*concat1, &concat1_array);
107 llvm_ir::IrArray::Index zero_2d({zero, zero}, shape, zero->getType());
108 concat1_array.EmitReadArrayElement(zero_2d, &b)
109 ->setName("read_concat1_array");
110 }
111
112 {
113 auto concat2_val = llvm::cast<llvm::GlobalVariable>(
114 ir_module.getOrInsertGlobal("concat2", array2d_type));
115 auto shape = ShapeUtil::MakeShape(F32, {2, 6});
116 llvm_ir::IrArray concat2_array(concat2_val, concat2_val->getValueType(),
117 shape);
118 aa.AddAliasingInformationToIrArray(*concat2, &concat2_array);
119 llvm_ir::IrArray::Index zero_2d({zero, zero}, shape, zero->getType());
120 concat2_array.EmitReadArrayElement(zero_2d, &b)
121 ->setName("read_concat2_array");
122 }
123
124 {
125 auto concat2_val = llvm::cast<llvm::GlobalVariable>(
126 ir_module.getOrInsertGlobal("add", array2d_type));
127 auto shape = ShapeUtil::MakeShape(F32, {2, 6});
128 llvm_ir::IrArray add_array(concat2_val, concat2_val->getValueType(), shape);
129 aa.AddAliasingInformationToIrArray(*add, &add_array);
130 llvm_ir::IrArray::Index zero_2d({zero, zero}, shape, zero->getType());
131 add_array.EmitReadArrayElement(zero_2d, &b)->setName("read_add_array");
132 }
133
134 // Check the AA info in the loads.
135 const char* filecheck_pattern = R"(
136 CHECK: %read_param_x_array = load {{.*}} !noalias [[param_x_noalias:![0-9]+]]
137 CHECK: %read_concat1_array = load {{.*}} !alias.scope [[concat1_scope:![0-9]+]], !noalias [[concat1_noalias:![0-9]+]]
138 CHECK: %read_concat2_array = load {{.*}} !alias.scope [[concat1_noalias]], !noalias [[concat1_scope]]
139 CHECK: %read_add_array = load {{.*}} !alias.scope [[concat1_noalias]]{{$}}
140 CHECK-DAG: [[buf_size32:![0-9]+]] = !{!"buffer:{{.*}} size:32
141 CHECK-DAG: [[buf_size48:![0-9]+]] = !{!"buffer:{{.*}} size:48
142 CHECK-DAG: [[param_x_noalias]] = !{[[buf_size48]], [[buf_size32]]}
143 CHECK-DAG: [[concat1_scope]] = !{[[buf_size32]]}
144 CHECK-DAG: [[concat1_noalias]] = !{[[buf_size48]]}
145 )";
146
147 TF_ASSERT_OK_AND_ASSIGN(
148 bool filecheck_match,
149 RunFileCheck(llvm_ir::DumpModuleToString(ir_module), filecheck_pattern));
150 EXPECT_TRUE(filecheck_match);
151 }
152
153 } // namespace cpu
154 } // namespace xla
155