1 : // Copyright 2013 Google Inc. 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 : // Unittests for memory access analysis.
16 :
17 : #include "syzygy/block_graph/analysis/memory_access_analysis.h"
18 :
19 : #include "gtest/gtest.h"
20 : #include "mnemonics.h" // NOLINT
21 :
22 : namespace block_graph {
23 : namespace analysis {
24 :
25 : namespace {
26 :
27 : typedef block_graph::analysis::MemoryAccessAnalysis::State State;
28 : typedef block_graph::BasicBlockSubGraph::BasicBlock BasicBlock;
29 : typedef block_graph::BasicBlockSubGraph::BasicBlock::Instructions Instructions;
30 : typedef BasicBlockSubGraph::BlockDescription BlockDescription;
31 :
32 : // _asm add eax, ecx
33 : const uint8 kClearEax[] = { 0x03, 0xC1 };
34 : // _asm add ecx, [eax]
35 : const uint8 kReadEax[] = { 0x03, 0x08 };
36 : // _asm add ecx, [eax + 42]
37 : const uint8 kReadEax42[] = { 0x03, 0x48, 0x2A };
38 : // _asm add eax, ecx
39 : const uint8 kRegsOnly[] = { 0x03, 0xC1 };
40 : // _asm add [eax + ebx*2 + 42], ecx
41 : const uint8 kWriteWithScale[] = { 0x01, 0x4C, 0x58, 0x2A };
42 : // _asm add DWORD PTR [X], ecx
43 : const uint8 kWriteDispl[] = { 0x01, 0x0D, 0x80, 0x1E, 0xF2, 0x00 };
44 : // _asm add [eax + 42], ecx
45 : const uint8 kWriteEax42[] = { 0x01, 0x48, 0x2A };
46 : // _asm lea ecx, [eax]
47 : const uint8 kLeaEax[] = { 0x8D, 0x08 };
48 : // _asm lea ecx, [eax + 42]
49 : const uint8 kLeaEax42[] = { 0x8D, 0x48, 0x2A };
50 :
51 : // _asm repnz movsb
52 : const uint8 kRepMovsb[] = { 0xF2, 0xA4 };
53 :
54 : // _asm add ecx, [eax + C]
55 : const uint8 kReadEax10[] = { 0x03, 0x48, 0x0A };
56 : const uint8 kReadEax11[] = { 0x03, 0x48, 0x0B };
57 : const uint8 kReadEax12[] = { 0x03, 0x48, 0x0C };
58 : const uint8 kReadEax13[] = { 0x03, 0x48, 0x0D };
59 : const uint8 kReadEax14[] = { 0x03, 0x48, 0x0E };
60 : const uint8 kReadEax15[] = { 0x03, 0x48, 0x0F };
61 :
62 : // _asm ret
63 : const uint8 kRet[] = { 0xC3 };
64 : // _asm call <FFFFFFFF>
65 : const uint8 kCall[] = { 0xE8, 0xFF, 0xD7, 0xFF, 0xFF };
66 : // _asm rdtsc
67 : const uint8 kRdtsc[] = { 0x0F, 0x31 };
68 :
69 : void AddSuccessorBetween(Successor::Condition condition,
70 : BasicCodeBlock* from,
71 E : BasicCodeBlock* to) {
72 : from->successors().push_back(
73 : Successor(condition,
74 : BasicBlockReference(BlockGraph::RELATIVE_REF,
75 : BlockGraph::Reference::kMaximumSize,
76 : to),
77 E : 0));
78 E : }
79 :
80 : } // namespace
81 :
82 : class TestMemoryAccessAnalysisState: public MemoryAccessAnalysis::State {
83 : public:
84 : bool IsEmpty(core::Register reg) const;
85 : bool IsEmpty() const;
86 : bool Contains(core::Register reg, int32 displ) const;
87 :
88 : template<size_t N>
89 : bool HasNonRedundantAccess(const uint8 (& data)[N]) const;
90 :
91 : template<size_t N>
92 : void Execute(const uint8 (& data)[N]);
93 :
94 : using MemoryAccessAnalysis::State::Clear;
95 : };
96 :
97 : class TestMemoryAccessAnalysis: public MemoryAccessAnalysis {
98 : public:
99 : using MemoryAccessAnalysis::Intersect;
100 : };
101 :
102 : class MemoryAccessAnalysisTest : public testing::Test {
103 : public:
104 E : MemoryAccessAnalysisTest() : bb_("Dummy") {}
105 :
106 E : bool Intersect(const block_graph::BasicBlock* bb, const State& state) {
107 E : return memory_access_.Intersect(bb, state);
108 E : }
109 :
110 E : void GetStateAtEntryOf(const BasicBlock* bb, State* state) const {
111 E : return memory_access_.GetStateAtEntryOf(bb, state);
112 E : }
113 :
114 : template<size_t N>
115 : void PropagateForward(const uint8 (& data)[N]);
116 :
117 : protected:
118 : TestMemoryAccessAnalysis memory_access_;
119 : TestMemoryAccessAnalysisState state_;
120 : BasicCodeBlock bb_;
121 : };
122 :
123 E : bool TestMemoryAccessAnalysisState::IsEmpty(core::Register reg) const {
124 E : DCHECK_LT(reg.code(), core::kRegisterMax);
125 E : return active_memory_accesses_[reg.code()].empty();
126 E : }
127 :
128 E : bool TestMemoryAccessAnalysisState::IsEmpty() const {
129 E : for (int r = 0; r < core::kRegisterMax; ++r) {
130 E : core::RegisterCode reg = core::RegisterCode(r);
131 E : if (!IsEmpty(core::Register(reg)))
132 E : return false;
133 E : }
134 E : return true;
135 E : }
136 :
137 : bool TestMemoryAccessAnalysisState::Contains(
138 E : core::Register reg, int32 displ) const {
139 E : DCHECK_LT(reg.code(), core::kRegisterMax);
140 E : const std::set<int32>& offsets = active_memory_accesses_[reg.code()];
141 E : return offsets.find(displ) != offsets.end();
142 E : }
143 :
144 : template<size_t N>
145 : bool TestMemoryAccessAnalysisState::HasNonRedundantAccess(
146 E : const uint8 (& data)[N]) const {
147 : // Decode an instruction.
148 E : DCHECK_GT(core::AssemblerImpl::kMaxInstructionLength, N);
149 :
150 E : block_graph::Instruction temp;
151 E : bool decoded = block_graph::Instruction::FromBuffer(&data[0], N, &temp);
152 E : DCHECK(decoded);
153 :
154 : // Expect to decode the entire buffer.
155 E : DCHECK(temp.size() == N);
156 :
157 : // Execute the decoded instruction, and modify the current state.
158 E : return MemoryAccessAnalysis::State::HasNonRedundantAccess(temp);
159 E : }
160 :
161 : template<size_t N>
162 E : void TestMemoryAccessAnalysisState::Execute(const uint8 (& data)[N]) {
163 : // Decode an instruction.
164 E : DCHECK_GT(core::AssemblerImpl::kMaxInstructionLength, N);
165 :
166 E : block_graph::Instruction temp;
167 E : bool decoded = block_graph::Instruction::FromBuffer(&data[0], N, &temp);
168 E : DCHECK(decoded);
169 :
170 : // Expect to decode the entire buffer.
171 E : DCHECK(temp.size() == N);
172 :
173 : // Execute the decoded instruction, and modify the current state.
174 E : MemoryAccessAnalysis::State::Execute(temp);
175 E : }
176 :
177 : template<size_t N>
178 : void MemoryAccessAnalysisTest::PropagateForward(
179 E : const uint8 (& data)[N]) {
180 : // Decode an instruction.
181 E : DCHECK_GT(core::AssemblerImpl::kMaxInstructionLength, N);
182 :
183 E : block_graph::Instruction temp;
184 E : bool decoded = block_graph::Instruction::FromBuffer(&data[0], N, &temp);
185 E : ASSERT_TRUE(decoded);
186 :
187 : // Expect to decode the entire buffer.
188 E : ASSERT_EQ(N, temp.size());
189 :
190 : // Execute the decoded instruction, and modify the current state.
191 E : MemoryAccessAnalysis::PropagateForward(temp, &state_);
192 E : }
193 :
194 E : TEST(MemoryAccessAnalysisStateTest, Constructor) {
195 : // Initial state is empty.
196 E : TestMemoryAccessAnalysisState state;
197 E : EXPECT_TRUE(state.IsEmpty());
198 E : }
199 :
200 E : TEST(MemoryAccessAnalysisStateTest, CopyConstructor) {
201 E : TestMemoryAccessAnalysisState state1;
202 :
203 E : state1.Execute(kReadEax);
204 E : state1.Execute(kWriteEax42);
205 :
206 E : EXPECT_TRUE(state1.Contains(core::eax, 0));
207 E : EXPECT_TRUE(state1.Contains(core::eax, 42));
208 :
209 : // Expect memory accesses to be copied into state2.
210 E : TestMemoryAccessAnalysisState state2(state1);
211 E : EXPECT_TRUE(state2.Contains(core::eax, 0));
212 E : EXPECT_TRUE(state2.Contains(core::eax, 42));
213 E : }
214 :
215 E : TEST(MemoryAccessAnalysisStateTest, Clear) {
216 E : TestMemoryAccessAnalysisState state;
217 :
218 E : state.Execute(kWriteEax42);
219 E : EXPECT_TRUE(!state.IsEmpty());
220 E : state.Clear();
221 E : EXPECT_TRUE(state.IsEmpty());
222 E : }
223 :
224 E : TEST(MemoryAccessAnalysisStateTest, ExecuteOperandKind) {
225 E : TestMemoryAccessAnalysisState state;
226 :
227 E : state.Execute(kRegsOnly);
228 E : EXPECT_TRUE(state.IsEmpty());
229 :
230 E : state.Execute(kWriteWithScale);
231 E : EXPECT_TRUE(state.IsEmpty());
232 :
233 E : state.Execute(kWriteDispl);
234 E : EXPECT_TRUE(state.IsEmpty());
235 :
236 E : state.Execute(kReadEax);
237 E : EXPECT_TRUE(state.Contains(core::eax, 0));
238 :
239 E : state.Execute(kReadEax42);
240 E : EXPECT_TRUE(state.Contains(core::eax, 42));
241 E : }
242 :
243 E : TEST(MemoryAccessAnalysisStateTest, LeaOperand) {
244 E : TestMemoryAccessAnalysisState state;
245 :
246 : // LEA do not perform a memory access.
247 E : state.Execute(kLeaEax);
248 E : EXPECT_FALSE(state.Contains(core::eax, 0));
249 E : state.Execute(kLeaEax42);
250 E : EXPECT_FALSE(state.Contains(core::eax, 42));
251 E : }
252 :
253 E : TEST(MemoryAccessAnalysisStateTest, ExecuteWithPrefix) {
254 E : TestMemoryAccessAnalysisState state;
255 :
256 E : state.Execute(kRepMovsb);
257 E : EXPECT_TRUE(state.IsEmpty());
258 E : }
259 :
260 E : TEST(MemoryAccessAnalysisStateTest, HasNonRedundantAccess) {
261 E : TestMemoryAccessAnalysisState state;
262 :
263 : // Initial state is empty, and accesses are not redundant.
264 E : bool redundant_read1 = state.HasNonRedundantAccess(kReadEax42);
265 E : bool redundant_write1 = state.HasNonRedundantAccess(kWriteEax42);
266 E : EXPECT_TRUE(redundant_read1);
267 E : EXPECT_TRUE(redundant_write1);
268 :
269 : // Perform a read of [eax + 42].
270 E : state.Execute(kReadEax42);
271 E : EXPECT_TRUE(state.Contains(core::eax, 42));
272 :
273 : // After the read, accesses are redundant.
274 E : bool redundant_read2 = state.HasNonRedundantAccess(kReadEax42);
275 E : bool redundant_write2 = state.HasNonRedundantAccess(kWriteEax42);
276 E : EXPECT_FALSE(redundant_read2);
277 E : EXPECT_FALSE(redundant_write2);
278 E : }
279 :
280 E : TEST(MemoryAccessAnalysisStateTest, HasNonRedundantAccessOperandKind) {
281 E : TestMemoryAccessAnalysisState state;
282 :
283 : // Instructions without memory access, on empty state.
284 E : EXPECT_FALSE(state.HasNonRedundantAccess(kRegsOnly));
285 E : EXPECT_FALSE(state.HasNonRedundantAccess(kLeaEax));
286 E : EXPECT_FALSE(state.HasNonRedundantAccess(kLeaEax42));
287 :
288 : // Initial state is empty, and accesses are not redundant.
289 E : EXPECT_TRUE(state.HasNonRedundantAccess(kReadEax));
290 E : EXPECT_TRUE(state.HasNonRedundantAccess(kReadEax42));
291 E : EXPECT_TRUE(state.HasNonRedundantAccess(kWriteWithScale));
292 E : EXPECT_TRUE(state.HasNonRedundantAccess(kWriteDispl));
293 E : EXPECT_TRUE(state.HasNonRedundantAccess(kWriteEax42));
294 E : }
295 :
296 E : TEST(MemoryAccessAnalysisStateTest, HasNonRedundantAccessWithPrefix) {
297 E : TestMemoryAccessAnalysisState state;
298 E : state.Execute(kRepMovsb);
299 E : bool redundant = state.HasNonRedundantAccess(kRepMovsb);
300 E : EXPECT_TRUE(redundant);
301 E : }
302 :
303 E : TEST_F(MemoryAccessAnalysisTest, GetStateOf) {
304 : // It is valid to get the state of a NULL block.
305 E : GetStateAtEntryOf(NULL, &state_);
306 E : EXPECT_TRUE(state_.IsEmpty());
307 :
308 : // Get an non-existing basic block.
309 E : GetStateAtEntryOf(&bb_, &state_);
310 E : EXPECT_TRUE(state_.IsEmpty());
311 E : }
312 :
313 E : TEST_F(MemoryAccessAnalysisTest, IntersectEmpty) {
314 E : memory_access_.GetStateAtEntryOf(&bb_, &state_);
315 E : EXPECT_TRUE(state_.IsEmpty());
316 :
317 : // Perform an initial intersection with an empty set.
318 E : TestMemoryAccessAnalysisState state;
319 E : Intersect(&bb_, state);
320 :
321 E : state.Execute(kReadEax10);
322 E : state.Execute(kReadEax11);
323 :
324 : // Perform an second intersection with a non empty set.
325 E : Intersect(&bb_, state_);
326 E : GetStateAtEntryOf(&bb_, &state_);
327 :
328 : // The results must be empty.
329 E : EXPECT_TRUE(state_.IsEmpty());
330 E : }
331 :
332 E : TEST_F(MemoryAccessAnalysisTest, IntersectStates) {
333 : // Intersection with displacements [10, 11, 12, 13, 14, 15].
334 E : TestMemoryAccessAnalysisState state1;
335 E : state1.Execute(kReadEax10);
336 E : state1.Execute(kReadEax11);
337 E : state1.Execute(kReadEax12);
338 E : state1.Execute(kReadEax13);
339 E : state1.Execute(kReadEax14);
340 E : state1.Execute(kReadEax15);
341 E : Intersect(&bb_, state1);
342 :
343 : // Intersection with displacements [10, 11, 12, 14].
344 E : TestMemoryAccessAnalysisState state2;
345 E : state2.Execute(kReadEax10);
346 E : state2.Execute(kReadEax11);
347 E : state2.Execute(kReadEax12);
348 E : state2.Execute(kReadEax14);
349 E : Intersect(&bb_, state2);
350 :
351 : // Check current state [10, 11, 12, 14].
352 E : GetStateAtEntryOf(&bb_, &state_);
353 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
354 E : EXPECT_TRUE(state_.Contains(core::eax, 11));
355 E : EXPECT_TRUE(state_.Contains(core::eax, 12));
356 E : EXPECT_FALSE(state_.Contains(core::eax, 13));
357 E : EXPECT_TRUE(state_.Contains(core::eax, 14));
358 E : EXPECT_FALSE(state_.Contains(core::eax, 15));
359 :
360 : // Intersection with displacements [10, 11, 15].
361 E : TestMemoryAccessAnalysisState state3;
362 E : state3.Execute(kReadEax10);
363 E : state3.Execute(kReadEax11);
364 E : state3.Execute(kReadEax15);
365 E : Intersect(&bb_, state3);
366 :
367 : // Check current state [10, 11].
368 E : GetStateAtEntryOf(&bb_, &state_);
369 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
370 E : EXPECT_TRUE(state_.Contains(core::eax, 11));
371 E : EXPECT_FALSE(state_.Contains(core::eax, 12));
372 E : EXPECT_FALSE(state_.Contains(core::eax, 13));
373 E : EXPECT_FALSE(state_.Contains(core::eax, 14));
374 E : EXPECT_FALSE(state_.Contains(core::eax, 15));
375 :
376 : // Intersection with displacements [15].
377 E : TestMemoryAccessAnalysisState state4;
378 E : state4.Execute(kReadEax15);
379 E : Intersect(&bb_, state4);
380 :
381 : // The state must be empty.
382 E : GetStateAtEntryOf(&bb_, &state_);
383 E : EXPECT_TRUE(state_.IsEmpty());
384 E : }
385 :
386 E : TEST_F(MemoryAccessAnalysisTest, PropagateForwardSimple) {
387 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kReadEax10));
388 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
389 :
390 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kReadEax));
391 E : EXPECT_TRUE(state_.Contains(core::eax, 0));
392 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
393 :
394 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kClearEax));
395 E : EXPECT_TRUE(state_.IsEmpty());
396 :
397 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kLeaEax));
398 E : EXPECT_TRUE(state_.IsEmpty());
399 E : }
400 :
401 E : TEST_F(MemoryAccessAnalysisTest, PropagateForwardWithCallRet) {
402 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kReadEax10));
403 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
404 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kCall));
405 E : EXPECT_TRUE(state_.IsEmpty());
406 :
407 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kReadEax10));
408 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
409 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kRet));
410 E : EXPECT_TRUE(state_.IsEmpty());
411 E : }
412 :
413 E : TEST_F(MemoryAccessAnalysisTest, UnknownInstruction) {
414 : // Ensure unknown instructions are processed correctly.
415 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kReadEax10));
416 E : EXPECT_TRUE(state_.Contains(core::eax, 10));
417 E : ASSERT_NO_FATAL_FAILURE(PropagateForward(kRdtsc));
418 E : EXPECT_TRUE(state_.IsEmpty());
419 E : }
420 :
421 E : TEST_F(MemoryAccessAnalysisTest, Analyze) {
422 E : BasicBlockSubGraph subgraph;
423 :
424 : BlockDescription* block = subgraph.AddBlockDescription(
425 E : "b1", BlockGraph::CODE_BLOCK, 7, 2, 42);
426 :
427 E : BasicCodeBlock* bb_if = subgraph.AddBasicCodeBlock("if");
428 E : BasicCodeBlock* bb_true = subgraph.AddBasicCodeBlock("true");
429 E : BasicCodeBlock* bb_false = subgraph.AddBasicCodeBlock("false");
430 E : BasicCodeBlock* bb_end = subgraph.AddBasicCodeBlock("end");
431 :
432 E : ASSERT_TRUE(bb_if != NULL);
433 E : ASSERT_TRUE(bb_true != NULL);
434 E : ASSERT_TRUE(bb_false != NULL);
435 E : ASSERT_TRUE(bb_end != NULL);
436 :
437 E : block->basic_block_order.push_back(bb_if);
438 E : block->basic_block_order.push_back(bb_true);
439 E : block->basic_block_order.push_back(bb_end);
440 E : block->basic_block_order.push_back(bb_false);
441 :
442 E : AddSuccessorBetween(Successor::kConditionEqual, bb_if, bb_true);
443 E : AddSuccessorBetween(Successor::kConditionNotEqual, bb_if, bb_false);
444 E : AddSuccessorBetween(Successor::kConditionTrue, bb_true, bb_end);
445 E : AddSuccessorBetween(Successor::kConditionTrue, bb_false, bb_end);
446 :
447 : BasicBlockAssembler asm_if(bb_if->instructions().end(),
448 E : &bb_if->instructions());
449 E : asm_if.mov(core::ecx, Operand(core::eax, Immediate(1, core::kSize32Bit)));
450 E : asm_if.mov(core::edx, Operand(core::ecx, Immediate(12, core::kSize32Bit)));
451 E : asm_if.mov(core::edx, Operand(core::eax, Immediate(42, core::kSize32Bit)));
452 :
453 : BasicBlockAssembler asm_true(bb_true->instructions().end(),
454 E : &bb_true->instructions());
455 E : asm_true.mov(core::ecx, Operand(core::eax, Immediate(1, core::kSize32Bit)));
456 E : asm_true.mov(core::edx, Operand(core::eax, Immediate(12, core::kSize32Bit)));
457 E : asm_true.mov(core::ecx, Operand(core::eax, Immediate(24, core::kSize32Bit)));
458 :
459 : BasicBlockAssembler asm_false(bb_false->instructions().end(),
460 E : &bb_false->instructions());
461 E : asm_false.mov(core::ecx, Operand(core::eax, Immediate(24, core::kSize32Bit)));
462 E : asm_false.mov(core::edx, Operand(core::eax, Immediate(48, core::kSize32Bit)));
463 :
464 : // Analyze the flow graph.
465 E : memory_access_.Analyze(&subgraph);
466 :
467 : // State of first basic block is empty.
468 E : GetStateAtEntryOf(bb_if, &state_);
469 E : EXPECT_TRUE(state_.IsEmpty());
470 :
471 : // Get entry state of bb_true.
472 E : GetStateAtEntryOf(bb_true, &state_);
473 E : EXPECT_TRUE(state_.Contains(core::eax, 1));
474 E : EXPECT_TRUE(state_.Contains(core::ecx, 12));
475 E : EXPECT_TRUE(state_.Contains(core::eax, 42));
476 :
477 : // Get entry state of bb_false.
478 E : GetStateAtEntryOf(bb_false, &state_);
479 E : EXPECT_TRUE(state_.Contains(core::eax, 1));
480 E : EXPECT_TRUE(state_.Contains(core::ecx, 12));
481 E : EXPECT_TRUE(state_.Contains(core::eax, 42));
482 :
483 : // Get entry state of bb_end. Intersection of bb_true and bb_false.
484 E : GetStateAtEntryOf(bb_end, &state_);
485 E : EXPECT_TRUE(state_.Contains(core::eax, 1));
486 E : EXPECT_FALSE(state_.Contains(core::eax, 12));
487 E : EXPECT_FALSE(state_.Contains(core::ecx, 12));
488 E : EXPECT_TRUE(state_.Contains(core::eax, 24));
489 E : EXPECT_TRUE(state_.Contains(core::eax, 42));
490 E : }
491 :
492 E : TEST_F(MemoryAccessAnalysisTest, AnalyzeWithData) {
493 E : BasicBlockSubGraph subgraph;
494 E : const uint8 raw_data[] = { 0, 1, 2, 3, 4 };
495 E : const size_t raw_data_len = ARRAYSIZE(raw_data);
496 :
497 : BlockDescription* block = subgraph.AddBlockDescription(
498 E : "b1", BlockGraph::CODE_BLOCK, 7, 2, 42);
499 :
500 E : BasicCodeBlock* bb = subgraph.AddBasicCodeBlock("bb");
501 : BasicDataBlock* data =
502 E : subgraph.AddBasicDataBlock("data", raw_data_len, &raw_data[0]);
503 :
504 E : block->basic_block_order.push_back(bb);
505 E : block->basic_block_order.push_back(data);
506 :
507 E : BasicBlockAssembler asm_bb(bb->instructions().end(), &bb->instructions());
508 E : asm_bb.ret();
509 :
510 : // Analyze the flow graph.
511 E : memory_access_.Analyze(&subgraph);
512 :
513 : // Expect empty state.
514 E : GetStateAtEntryOf(bb, &state_);
515 E : EXPECT_TRUE(state_.IsEmpty());
516 :
517 E : GetStateAtEntryOf(data, &state_);
518 E : EXPECT_TRUE(state_.IsEmpty());
519 E : }
520 :
521 : } // namespace analysis
522 : } // namespace block_graph
|