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 : #include "syzygy/optimize/transforms/peephole_transform.h"
16 :
17 : #include "gmock/gmock.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/block_graph/basic_block_decomposer.h"
20 : #include "syzygy/block_graph/basic_block_subgraph.h"
21 : #include "syzygy/block_graph/block_builder.h"
22 : #include "syzygy/block_graph/block_graph.h"
23 : #include "syzygy/optimize/application_profile.h"
24 : #include "syzygy/pe/pe_transform_policy.h"
25 :
26 : namespace optimize {
27 : namespace transforms {
28 :
29 : namespace {
30 :
31 : using block_graph::BasicBlock;
32 : using block_graph::BasicBlockDecomposer;
33 : using block_graph::BasicBlockSubGraph;
34 : using block_graph::BasicCodeBlock;
35 : using block_graph::BlockBuilder;
36 : using block_graph::BlockGraph;
37 : using pe::ImageLayout;
38 : using testing::ElementsAreArray;
39 : typedef BasicBlock::Instructions Instructions;
40 :
41 : // _asm push ebp
42 : // _asm mov ebp, esp
43 : // _asm pop ebp
44 : // _asm xor eax, eax
45 : // _asm ret
46 : const uint8 kPrologEpilog[] = { 0x55, 0x8B, 0xEC, 0x5D, 0x33, 0xC0, 0xC3 };
47 :
48 : const uint8 kTwicePrologEpilog[] =
49 : { 0x55, 0x8B, 0xEC, 0x5D, 0x55, 0x8B, 0xEC, 0x5D, 0x33, 0xC0, 0xC3 };
50 :
51 : // _asm ret
52 : const uint8 kRet[] = { 0xC3 };
53 :
54 : // _asm xor eax, eax
55 : // _asm ret
56 : const uint8 kRet0[] = { 0x33, 0xC0, 0xC3 };
57 :
58 : // _asm mov ecx, ecx
59 : // _asm ret
60 : const uint8 kMovIdentity[] = { 0x8B, 0xC9, 0xC3 };
61 :
62 : enum TransformKind {
63 : ktransformBlock,
64 : ktransformSubgraph
65 : };
66 :
67 : class PeepholeTransformTest : public testing::Test {
68 : public:
69 : PeepholeTransformTest()
70 : : image_(&block_graph_),
71 : profile_(&image_),
72 E : block_(NULL) {
73 E : }
74 :
75 : void TransformBlock(TransformKind kind, const uint8* data, size_t length);
76 :
77 : protected:
78 : pe::PETransformPolicy policy_;
79 : BlockGraph block_graph_;
80 : ImageLayout image_;
81 : BlockGraph::Block* block_;
82 : PeepholeTransform tx_;
83 : ApplicationProfile profile_;
84 : SubGraphProfile subgraph_profile_;
85 : };
86 :
87 : void PeepholeTransformTest::TransformBlock(TransformKind kind,
88 : const uint8* data,
89 E : size_t length) {
90 E : DCHECK_NE(reinterpret_cast<const uint8*>(NULL), data);
91 :
92 : // Create a dummy block.
93 E : block_ = block_graph_.AddBlock(BlockGraph::CODE_BLOCK, length, "test");
94 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), block_);
95 E : block_->SetData(data, length);
96 E : block_->SetLabel(0, "code", BlockGraph::CODE_LABEL);
97 :
98 : // Decompose to subgraph.
99 E : BasicBlockSubGraph subgraph;
100 E : BasicBlockDecomposer decomposer(block_, &subgraph);
101 E : ASSERT_TRUE(decomposer.Decompose());
102 :
103 E : switch (kind) {
104 : case ktransformBlock: {
105 : // Apply peephole transform.
106 E : PeepholeTransform tx;
107 : ASSERT_TRUE(
108 : tx_.TransformBasicBlockSubGraph(&policy_, &block_graph_, &subgraph,
109 E : &profile_, &subgraph_profile_));
110 E : break;
111 : }
112 : case ktransformSubgraph: {
113 : // Apply peephole simplification on subgraph.
114 E : ASSERT_TRUE(PeepholeTransform::SimplifySubgraph(&subgraph));
115 : break;
116 : }
117 : }
118 :
119 : // Rebuild block.
120 E : BlockBuilder builder(&block_graph_);
121 E : ASSERT_TRUE(builder.Merge(&subgraph));
122 E : CHECK_EQ(1u, builder.new_blocks().size());
123 E : block_ = *builder.new_blocks().begin();
124 E : };
125 :
126 : } // namespace
127 :
128 E : TEST_F(PeepholeTransformTest, SimplifyEmptyPrologEpilogBlock) {
129 : ASSERT_NO_FATAL_FAILURE(
130 E : TransformBlock(ktransformBlock, kPrologEpilog, sizeof(kPrologEpilog)));
131 E : EXPECT_THAT(kRet0, ElementsAreArray(block_->data(), block_->size()));
132 E : }
133 :
134 E : TEST_F(PeepholeTransformTest, SimplifyEmptyPrologEpilogSubgraph) {
135 : ASSERT_NO_FATAL_FAILURE(
136 E : TransformBlock(ktransformSubgraph, kPrologEpilog, sizeof(kPrologEpilog)));
137 E : EXPECT_THAT(kRet0, ElementsAreArray(block_->data(), block_->size()));
138 E : }
139 :
140 E : TEST_F(PeepholeTransformTest, SimplifyEmptyPrologEpilogTwice) {
141 : ASSERT_NO_FATAL_FAILURE(
142 : TransformBlock(ktransformBlock,
143 : kTwicePrologEpilog,
144 E : sizeof(kTwicePrologEpilog)));
145 E : EXPECT_THAT(kRet0, ElementsAreArray(block_->data(), block_->size()));
146 E : }
147 :
148 E : TEST_F(PeepholeTransformTest, SimplifyIdentityMov) {
149 : ASSERT_NO_FATAL_FAILURE(
150 : TransformBlock(ktransformBlock,
151 : kMovIdentity,
152 E : sizeof(kMovIdentity)));
153 E : EXPECT_THAT(kRet, ElementsAreArray(block_->data(), block_->size()));
154 E : }
155 :
156 E : TEST_F(PeepholeTransformTest, RemoveDeadCodeSubgraph) {
157 : // _asm mov eax, 4
158 : // _asm cmp eax, edx
159 : // _asm cmp edx, 0
160 : // _asm inc edx
161 : // _asm xor edx, edx
162 : // _asm cmp edx, 0
163 : // _asm ret
164 : const uint8 kSource[] = { 0xB8, 0x04, 0x00, 0x00, 0x00, 0x3B, 0xC2, 0x83,
165 E : 0xFA, 0x00, 0x42, 0x33, 0xD2, 0x83, 0xFA, 0x00, 0xC3 };
166 :
167 : // _asm mov eax, 4
168 : // _asm xor edx, edx
169 : // _asm cmp edx, 0
170 : // _asm ret
171 : const uint8 kResult[] = { 0xB8, 0x04, 0x00, 0x00, 0x00, 0x33, 0xD2, 0x83,
172 E : 0xFA, 0x00, 0xC3 };
173 :
174 : ASSERT_NO_FATAL_FAILURE(
175 E : TransformBlock(ktransformBlock, kSource, sizeof(kSource)));
176 E : EXPECT_THAT(kResult, ElementsAreArray(block_->data(), block_->size()));
177 E : }
178 :
179 E : TEST_F(PeepholeTransformTest, RemoveDeadCodeSubgraphWithStackManipulation) {
180 : // _asm push 1
181 : // _asm pop ecx
182 : // _asm xor ecx, ecx
183 : // _asm ret
184 E : const uint8 kSource[] = { 0x6A, 0x01, 0x59, 0x33, 0xC9, 0xC3 };
185 :
186 : ASSERT_NO_FATAL_FAILURE(
187 E : TransformBlock(ktransformBlock, kSource, sizeof(kSource)));
188 :
189 : // This code is not simplified. There is some stack manipulation.
190 E : EXPECT_THAT(kSource, ElementsAreArray(block_->data(), block_->size()));
191 E : }
192 :
193 E : TEST_F(PeepholeTransformTest, RemoveDeadCodeSubgraphWith8BitRegister) {
194 : // _asm inc al
195 : // _asm xor eax, eax
196 : // _asm ret
197 E : const uint8 kSource[] = { 0xFE, 0xC0, 0x33, 0xC0, 0xC3 };
198 :
199 : ASSERT_NO_FATAL_FAILURE(
200 E : TransformBlock(ktransformBlock, kSource, sizeof(kSource)));
201 :
202 : // This code is not simplified. There is a 8-bit register.
203 E : EXPECT_THAT(kSource, ElementsAreArray(block_->data(), block_->size()));
204 E : }
205 :
206 : } // namespace transforms
207 : } // namespace optimize
|