1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <tuple>
18
19 #include "gtest/gtest.h"
20
21 #include "berberis/backend/x86_64/insn_folding.h"
22
23 #include "berberis/backend/code_emitter.h" // for CodeEmitter::Condition
24 #include "berberis/backend/x86_64/machine_ir.h"
25 #include "berberis/backend/x86_64/machine_ir_builder.h"
26 #include "berberis/base/arena_alloc.h"
27 #include "berberis/guest_state/guest_addr.h"
28
29 namespace berberis::x86_64 {
30
31 namespace {
32
33 // By default for the successful folding the immediate must be sign-extended from 32-bit to the same
34 // 64-bit integer number.
35 template <typename InsnTypeRegReg, typename InsnTypeRegImm, bool kExpectSuccess = true>
TryRegRegInsnFolding(bool is_64bit_mov_imm,uint64_t imm=0x7777ffffULL)36 void TryRegRegInsnFolding(bool is_64bit_mov_imm, uint64_t imm = 0x7777ffffULL) {
37 Arena arena;
38 MachineIR machine_ir(&arena);
39 auto* bb = machine_ir.NewBasicBlock();
40
41 MachineIRBuilder builder(&machine_ir);
42
43 MachineReg vreg1 = machine_ir.AllocVReg();
44 MachineReg vreg2 = machine_ir.AllocVReg();
45 MachineReg flags = machine_ir.AllocVReg();
46
47 builder.StartBasicBlock(bb);
48 if (is_64bit_mov_imm) {
49 builder.Gen<MovqRegImm>(vreg1, imm);
50 } else {
51 builder.Gen<MovlRegImm>(vreg1, imm);
52 }
53 builder.Gen<InsnTypeRegReg>(vreg2, vreg1, flags);
54 builder.Gen<PseudoJump>(kNullGuestAddr);
55
56 bb->live_out().push_back(vreg2);
57
58 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
59 for (const auto* insn : bb->insn_list()) {
60 def_map.ProcessInsn(insn);
61 }
62
63 InsnFolding insn_folding(def_map, &machine_ir);
64
65 auto insn_it = bb->insn_list().begin();
66 insn_it++;
67 const MachineInsn* insn = *insn_it;
68
69 auto [is_folded, folded_insn] = insn_folding.TryFoldInsn(insn);
70
71 if (!is_folded) {
72 EXPECT_FALSE(kExpectSuccess);
73 return;
74 }
75 EXPECT_TRUE(kExpectSuccess);
76 EXPECT_EQ(InsnTypeRegImm::kInfo.opcode, folded_insn->opcode());
77 EXPECT_EQ(vreg2, folded_insn->RegAt(0));
78 EXPECT_EQ(flags, folded_insn->RegAt(1));
79 EXPECT_EQ(static_cast<uint64_t>(static_cast<int32_t>(imm)),
80 AsMachineInsnX86_64(folded_insn)->imm());
81 }
82
83 template <typename InsnTypeRegReg, typename InsnTypeRegImm>
TryMovInsnFolding(bool is_64bit_mov_imm,uint64_t imm)84 void TryMovInsnFolding(bool is_64bit_mov_imm, uint64_t imm) {
85 Arena arena;
86 MachineIR machine_ir(&arena);
87 auto* bb = machine_ir.NewBasicBlock();
88
89 MachineIRBuilder builder(&machine_ir);
90
91 MachineReg vreg1 = machine_ir.AllocVReg();
92 MachineReg vreg2 = machine_ir.AllocVReg();
93
94 builder.StartBasicBlock(bb);
95 if (is_64bit_mov_imm) {
96 builder.Gen<MovqRegImm>(vreg1, imm);
97 } else {
98 builder.Gen<MovlRegImm>(vreg1, imm);
99 }
100 builder.Gen<InsnTypeRegReg>(vreg2, vreg1);
101 builder.Gen<PseudoJump>(kNullGuestAddr);
102
103 bb->live_out().push_back(vreg2);
104
105 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
106 for (const auto* insn : bb->insn_list()) {
107 def_map.ProcessInsn(insn);
108 }
109
110 InsnFolding insn_folding(def_map, &machine_ir);
111
112 auto insn_it = bb->insn_list().begin();
113 insn_it++;
114 const MachineInsn* insn = *insn_it;
115
116 auto [is_folded, folded_insn] = insn_folding.TryFoldInsn(insn);
117
118 EXPECT_TRUE(is_folded);
119 EXPECT_EQ(InsnTypeRegImm::kInfo.opcode, folded_insn->opcode());
120 EXPECT_EQ(vreg2, folded_insn->RegAt(0));
121 // MovqRegReg is the only instruction that can take full 64-bit imm.
122 if (InsnTypeRegReg::kInfo.opcode == MovqRegReg::kInfo.opcode) {
123 // Take into account zero-extension when MOVL.
124 EXPECT_EQ(is_64bit_mov_imm ? imm : static_cast<uint32_t>(imm),
125 AsMachineInsnX86_64(folded_insn)->imm());
126 } else {
127 EXPECT_EQ(static_cast<uint64_t>(static_cast<int32_t>(imm)),
128 AsMachineInsnX86_64(folded_insn)->imm());
129 }
130 }
131
TEST(InsnFoldingTest,DefMapGetsLatestDef)132 TEST(InsnFoldingTest, DefMapGetsLatestDef) {
133 Arena arena;
134 MachineIR machine_ir(&arena);
135
136 auto* bb = machine_ir.NewBasicBlock();
137
138 MachineIRBuilder builder(&machine_ir);
139
140 MachineReg vreg1 = machine_ir.AllocVReg();
141 MachineReg vreg2 = machine_ir.AllocVReg();
142 MachineReg flags = machine_ir.AllocVReg();
143
144 builder.StartBasicBlock(bb);
145 builder.Gen<MovqRegImm>(vreg1, 0);
146 builder.Gen<MovqRegImm>(vreg2, 0);
147 builder.Gen<AddqRegReg>(vreg2, vreg1, flags);
148 builder.Gen<PseudoJump>(kNullGuestAddr);
149
150 bb->live_out().push_back(vreg1);
151 bb->live_out().push_back(vreg2);
152
153 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
154 for (const auto* insn : bb->insn_list()) {
155 def_map.ProcessInsn(insn);
156 }
157
158 auto [vreg1_def, index1] = def_map.Get(vreg1);
159 EXPECT_EQ(kMachineOpMovqRegImm, vreg1_def->opcode());
160 EXPECT_EQ(vreg1, vreg1_def->RegAt(0));
161 EXPECT_EQ(index1, 0);
162
163 auto [vreg2_def, index2] = def_map.Get(vreg2);
164 EXPECT_EQ(kMachineOpAddqRegReg, vreg2_def->opcode());
165 EXPECT_EQ(vreg2, vreg2_def->RegAt(0));
166 EXPECT_EQ(index2, 2);
167 }
168
TEST(InsnFoldingTest,MovFolding)169 TEST(InsnFoldingTest, MovFolding) {
170 constexpr uint64_t kSignExtendableImm = 0xffff'ffff'8000'0000ULL;
171 constexpr uint64_t kNotSignExtendableImm = 0xffff'ffff'0000'0000ULL;
172 for (bool is_64bit_mov_imm : {true, false}) {
173 // MovqRegReg is the only instruction that allow 64-bit immediates.
174 TryMovInsnFolding<MovqRegReg, MovqRegImm>(is_64bit_mov_imm, kSignExtendableImm);
175 TryMovInsnFolding<MovqRegReg, MovqRegImm>(is_64bit_mov_imm, kNotSignExtendableImm);
176 // Movl isn't sensetive to upper immediate bits.
177 TryMovInsnFolding<MovlRegReg, MovlRegImm>(is_64bit_mov_imm, kSignExtendableImm);
178 TryMovInsnFolding<MovlRegReg, MovlRegImm>(is_64bit_mov_imm, kNotSignExtendableImm);
179 }
180 }
181
TEST(InsnFoldingTest,SingleMovqMemBaseDispImm32Folding)182 TEST(InsnFoldingTest, SingleMovqMemBaseDispImm32Folding) {
183 Arena arena;
184 MachineIR machine_ir(&arena);
185
186 MachineIRBuilder builder(&machine_ir);
187
188 auto* bb = machine_ir.NewBasicBlock();
189 auto* recovery_bb = machine_ir.NewBasicBlock();
190
191 MachineReg vreg1 = machine_ir.AllocVReg();
192
193 builder.StartBasicBlock(bb);
194 builder.Gen<MovlRegImm>(vreg1, 2);
195 builder.Gen<MovqMemBaseDispReg>(kMachineRegRAX, 4, vreg1);
196 builder.SetRecoveryPointAtLastInsn(recovery_bb);
197 builder.SetRecoveryWithGuestPCAtLastInsn(42);
198 builder.Gen<PseudoJump>(kNullGuestAddr);
199
200 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
201 for (const auto* insn : bb->insn_list()) {
202 def_map.ProcessInsn(insn);
203 }
204
205 InsnFolding insn_folding(def_map, &machine_ir);
206
207 auto insn_it = bb->insn_list().begin();
208 insn_it++;
209 const MachineInsn* insn = *insn_it;
210
211 auto [_, folded_insn] = insn_folding.TryFoldInsn(insn);
212 EXPECT_EQ(kMachineOpMovqMemBaseDispImm, folded_insn->opcode());
213 EXPECT_EQ(kMachineRegRAX, folded_insn->RegAt(0));
214 EXPECT_EQ(2UL, AsMachineInsnX86_64(folded_insn)->imm());
215 EXPECT_EQ(4UL, AsMachineInsnX86_64(folded_insn)->disp());
216 EXPECT_EQ(folded_insn->recovery_pc(), 42UL);
217 EXPECT_EQ(folded_insn->recovery_bb(), recovery_bb);
218 }
219
TEST(InsnFoldingTest,SingleMovlMemBaseDispImm32Folding)220 TEST(InsnFoldingTest, SingleMovlMemBaseDispImm32Folding) {
221 Arena arena;
222 MachineIR machine_ir(&arena);
223
224 MachineIRBuilder builder(&machine_ir);
225
226 auto* bb = machine_ir.NewBasicBlock();
227 auto* recovery_bb = machine_ir.NewBasicBlock();
228
229 MachineReg vreg1 = machine_ir.AllocVReg();
230
231 builder.StartBasicBlock(bb);
232 builder.Gen<MovqRegImm>(vreg1, 0x3'0000'0003);
233 builder.Gen<MovlMemBaseDispReg>(kMachineRegRAX, 4, vreg1);
234 builder.SetRecoveryPointAtLastInsn(recovery_bb);
235 builder.SetRecoveryWithGuestPCAtLastInsn(42);
236 builder.Gen<PseudoJump>(kNullGuestAddr);
237
238 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
239 for (const auto* insn : bb->insn_list()) {
240 def_map.ProcessInsn(insn);
241 }
242
243 InsnFolding insn_folding(def_map, &machine_ir);
244
245 auto insn_it = bb->insn_list().begin();
246 insn_it++;
247 const MachineInsn* insn = *insn_it;
248
249 auto [_, folded_insn] = insn_folding.TryFoldInsn(insn);
250 EXPECT_EQ(kMachineOpMovlMemBaseDispImm, folded_insn->opcode());
251 EXPECT_EQ(kMachineRegRAX, folded_insn->RegAt(0));
252 EXPECT_EQ(3UL, AsMachineInsnX86_64(folded_insn)->imm());
253 EXPECT_EQ(4UL, AsMachineInsnX86_64(folded_insn)->disp());
254 EXPECT_EQ(folded_insn->recovery_pc(), 42UL);
255 EXPECT_EQ(folded_insn->recovery_bb(), recovery_bb);
256 }
257
TEST(InsnFoldingTest,RedundantMovlFolding)258 TEST(InsnFoldingTest, RedundantMovlFolding) {
259 Arena arena;
260 MachineIR machine_ir(&arena);
261
262 MachineIRBuilder builder(&machine_ir);
263
264 auto* bb = machine_ir.NewBasicBlock();
265
266 MachineReg vreg1 = machine_ir.AllocVReg();
267 MachineReg vreg2 = machine_ir.AllocVReg();
268 MachineReg vreg3 = machine_ir.AllocVReg();
269 MachineReg flags = machine_ir.AllocVReg();
270
271 builder.StartBasicBlock(bb);
272 builder.Gen<AddlRegReg>(vreg2, vreg3, flags);
273 builder.Gen<MovlRegReg>(vreg1, vreg2);
274 builder.Gen<PseudoJump>(kNullGuestAddr);
275
276 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
277 for (const auto* insn : bb->insn_list()) {
278 def_map.ProcessInsn(insn);
279 }
280
281 InsnFolding insn_folding(def_map, &machine_ir);
282
283 auto insn_it = bb->insn_list().begin();
284 const MachineInsn* insn = *std::next(insn_it);
285
286 auto [_, folded_insn] = insn_folding.TryFoldInsn(insn);
287 EXPECT_EQ(kMachineOpPseudoCopy, folded_insn->opcode());
288 EXPECT_EQ(vreg1, folded_insn->RegAt(0));
289 EXPECT_EQ(vreg2, folded_insn->RegAt(1));
290 }
291
TEST(InsnFoldingTest,GracefulHandlingOfVRegDefinedInPreviousBasicBlock)292 TEST(InsnFoldingTest, GracefulHandlingOfVRegDefinedInPreviousBasicBlock) {
293 Arena arena;
294 MachineIR machine_ir(&arena);
295
296 MachineIRBuilder builder(&machine_ir);
297
298 auto* bb = machine_ir.NewBasicBlock();
299
300 MachineReg vreg1 = machine_ir.AllocVReg();
301 MachineReg vreg2 = machine_ir.AllocVReg();
302
303 bb->live_in().push_back(vreg2);
304
305 builder.StartBasicBlock(bb);
306 builder.Gen<MovlRegReg>(vreg1, vreg2);
307 builder.Gen<PseudoJump>(kNullGuestAddr);
308
309 DefMap def_map(machine_ir.NumVReg(), machine_ir.arena());
310 for (const auto* insn : bb->insn_list()) {
311 def_map.ProcessInsn(insn);
312 }
313
314 InsnFolding insn_folding(def_map, &machine_ir);
315
316 const MachineInsn* insn = *(bb->insn_list().begin());
317
318 auto [success, _] = insn_folding.TryFoldInsn(insn);
319 EXPECT_FALSE(success);
320 }
321
TEST(InsnFoldingTest,RegRegInsnTypeFolding)322 TEST(InsnFoldingTest, RegRegInsnTypeFolding) {
323 for (bool is_64bit_mov_imm : {true, false}) {
324 TryRegRegInsnFolding<AddqRegReg, AddqRegImm>(is_64bit_mov_imm);
325 TryRegRegInsnFolding<SubqRegReg, SubqRegImm>(is_64bit_mov_imm);
326 TryRegRegInsnFolding<CmpqRegReg, CmpqRegImm>(is_64bit_mov_imm);
327 TryRegRegInsnFolding<OrqRegReg, OrqRegImm>(is_64bit_mov_imm);
328 TryRegRegInsnFolding<XorqRegReg, XorqRegImm>(is_64bit_mov_imm);
329 TryRegRegInsnFolding<AndqRegReg, AndqRegImm>(is_64bit_mov_imm);
330 TryRegRegInsnFolding<TestqRegReg, TestqRegImm>(is_64bit_mov_imm);
331
332 TryRegRegInsnFolding<AddlRegReg, AddlRegImm>(is_64bit_mov_imm);
333 TryRegRegInsnFolding<SublRegReg, SublRegImm>(is_64bit_mov_imm);
334 TryRegRegInsnFolding<CmplRegReg, CmplRegImm>(is_64bit_mov_imm);
335 TryRegRegInsnFolding<OrlRegReg, OrlRegImm>(is_64bit_mov_imm);
336 TryRegRegInsnFolding<XorlRegReg, XorlRegImm>(is_64bit_mov_imm);
337 TryRegRegInsnFolding<AndlRegReg, AndlRegImm>(is_64bit_mov_imm);
338 TryRegRegInsnFolding<TestlRegReg, TestlRegImm>(is_64bit_mov_imm);
339 }
340 }
341
342 TEST(InsnFoldingTest, 32To64SignExtendableImm) {
343 // The signed immediate is 32->64 sign-extend to the same integer value.
344 constexpr uint64_t kImm = 0xffff'ffff'8000'0000ULL;
345 // Can fold into 64-bit instruction.
346 TryRegRegInsnFolding<AddqRegReg,
347 AddqRegImm,
348 /* kExpectSuccess */ true>(/* is_64bit_mov_imm */ true, kImm);
349 // But cannot fold if the upper bits are cleared out by MOVL, since it's not sign-extable anymore.
350 TryRegRegInsnFolding<AddqRegReg,
351 AddqRegImm,
352 /* kExpectSuccess */ false>(/* is_64bit_mov_imm */ false, kImm);
353
354 for (bool is_64bit_mov_imm : {true, false}) {
355 // Can fold into 32-bit instruction since the upper bits are not used.
356 TryRegRegInsnFolding<AddlRegReg,
357 AddlRegImm,
358 /* kExpectSuccess */ true>(is_64bit_mov_imm, kImm);
359 }
360 }
361
TEST(InsnFoldingTest,Not32To64SignExtendableImm)362 TEST(InsnFoldingTest, Not32To64SignExtendableImm) {
363 // The immediate doesn't 32->64 sign-extend to the same integer value.
364 constexpr uint64_t kImm = 0xffff'ffff'0000'0000ULL;
365 // Cannot fold into 64-bit instruction.
366 TryRegRegInsnFolding<AddqRegReg,
367 AddqRegImm,
368 /* kExpectSuccess */ false>(/* is_64bit_mov_imm */ true, kImm);
369 // But can fold if the upper bits are cleared out by MOVL.
370 TryRegRegInsnFolding<AddqRegReg,
371 AddqRegImm,
372 /* kExpectSuccess */ true>(/* is_64bit_mov_imm */ false, kImm);
373
374 for (bool is_64bit_mov_imm : {true, false}) {
375 // Can fold into 32-bit instruction since the upper bits are not used.
376 TryRegRegInsnFolding<AddlRegReg,
377 AddlRegImm,
378 /* kExpectSuccess */ true>(is_64bit_mov_imm, kImm);
379 }
380 }
381
TEST(InsnFoldingTest,HardRegsAreSafe)382 TEST(InsnFoldingTest, HardRegsAreSafe) {
383 Arena arena;
384 MachineIR machine_ir(&arena);
385
386 auto* bb = machine_ir.NewBasicBlock();
387
388 MachineIRBuilder builder(&machine_ir);
389
390 builder.StartBasicBlock(bb);
391 builder.Gen<AddqRegReg>(kMachineRegRAX, kMachineRegRDI, kMachineRegFLAGS);
392 builder.Gen<PseudoJump>(kNullGuestAddr);
393
394 FoldInsns(&machine_ir);
395
396 EXPECT_EQ(bb->insn_list().size(), 2UL);
397 }
398
TEST(InsnFoldingTest,PseudoWriteFlagsErased)399 TEST(InsnFoldingTest, PseudoWriteFlagsErased) {
400 Arena arena;
401 MachineIR machine_ir(&arena);
402
403 MachineIRBuilder builder(&machine_ir);
404
405 auto* bb = machine_ir.NewBasicBlock();
406
407 MachineReg flag = machine_ir.AllocVReg();
408 MachineReg vreg2 = machine_ir.AllocVReg();
409 MachineReg vreg3 = machine_ir.AllocVReg();
410 MachineReg vreg4 = machine_ir.AllocVReg();
411 MachineReg vreg5 = machine_ir.AllocVReg();
412
413 builder.StartBasicBlock(bb);
414 builder.Gen<AddqRegReg>(vreg4, vreg5, flag);
415 builder.Gen<PseudoReadFlags>(PseudoReadFlags::kWithOverflow, vreg2, flag);
416 builder.Gen<PseudoCopy>(vreg3, vreg2, 8);
417 builder.Gen<PseudoWriteFlags>(vreg3, flag);
418 builder.Gen<PseudoJump>(kNullGuestAddr);
419
420 FoldInsns(&machine_ir);
421
422 EXPECT_EQ(bb->insn_list().size(), 4UL);
423
424 auto insn_it = bb->insn_list().rbegin();
425 insn_it++;
426 const MachineInsn* insn = *insn_it;
427
428 EXPECT_EQ(kMachineOpPseudoCopy, insn->opcode());
429 }
430
TEST(InsnFoldingTest,FlagModifiedAfterPseudoRead)431 TEST(InsnFoldingTest, FlagModifiedAfterPseudoRead) {
432 Arena arena;
433 MachineIR machine_ir(&arena);
434
435 MachineIRBuilder builder(&machine_ir);
436
437 auto* bb = machine_ir.NewBasicBlock();
438
439 MachineReg flag = machine_ir.AllocVReg();
440 MachineReg vreg2 = machine_ir.AllocVReg();
441 MachineReg vreg3 = machine_ir.AllocVReg();
442 MachineReg vreg4 = machine_ir.AllocVReg();
443 MachineReg vreg5 = machine_ir.AllocVReg();
444
445 builder.StartBasicBlock(bb);
446 builder.Gen<PseudoReadFlags>(PseudoReadFlags::kWithOverflow, vreg2, flag);
447 builder.Gen<PseudoCopy>(vreg3, vreg2, 8);
448 builder.Gen<AddqRegReg>(vreg4, vreg5, flag);
449 builder.Gen<PseudoWriteFlags>(vreg3, flag);
450 builder.Gen<PseudoJump>(kNullGuestAddr);
451
452 FoldInsns(&machine_ir);
453
454 EXPECT_EQ(bb->insn_list().size(), 5UL);
455 }
456
TEST(InsnFoldingTest,WriteFlagsNotDeletedBecauseDefinitionIsAfterUse)457 TEST(InsnFoldingTest, WriteFlagsNotDeletedBecauseDefinitionIsAfterUse) {
458 Arena arena;
459 MachineIR machine_ir(&arena);
460
461 MachineIRBuilder builder(&machine_ir);
462
463 auto* bb = machine_ir.NewBasicBlock();
464
465 MachineReg flag = machine_ir.AllocVReg();
466 MachineReg vreg2 = machine_ir.AllocVReg();
467 MachineReg vreg3 = machine_ir.AllocVReg();
468
469 builder.StartBasicBlock(bb);
470 builder.Gen<PseudoReadFlags>(PseudoReadFlags::kWithOverflow, vreg2, flag);
471 builder.Gen<PseudoCopy>(vreg3, vreg2, 8);
472 builder.Gen<MovqRegImm>(vreg2, 3);
473 builder.Gen<PseudoWriteFlags>(vreg3, flag);
474 builder.Gen<PseudoJump>(kNullGuestAddr);
475
476 FoldInsns(&machine_ir);
477
478 EXPECT_EQ(bb->insn_list().size(), 5UL);
479 }
480
TEST(InsnFoldingTest,FoldInsnsSmoke)481 TEST(InsnFoldingTest, FoldInsnsSmoke) {
482 Arena arena;
483 MachineIR machine_ir(&arena);
484
485 auto* bb = machine_ir.NewBasicBlock();
486
487 MachineIRBuilder builder(&machine_ir);
488
489 MachineReg vreg1 = machine_ir.AllocVReg();
490 MachineReg vreg2 = machine_ir.AllocVReg();
491 MachineReg flags = machine_ir.AllocVReg();
492
493 builder.StartBasicBlock(bb);
494 builder.Gen<MovqRegImm>(vreg1, 2);
495 builder.Gen<AddqRegReg>(vreg2, vreg1, flags);
496 builder.Gen<PseudoJump>(kNullGuestAddr);
497
498 bb->live_out().push_back(vreg2);
499 bb->live_in().push_back(vreg2);
500
501 FoldInsns(&machine_ir);
502
503 EXPECT_EQ(bb->insn_list().size(), 3UL);
504
505 auto insn_it = bb->insn_list().begin();
506 insn_it++;
507 MachineInsn* insn = *insn_it;
508
509 EXPECT_EQ(insn->opcode(), kMachineOpAddqRegImm);
510 EXPECT_EQ(vreg2, insn->RegAt(0));
511 EXPECT_EQ(2UL, AsMachineInsnX86_64(insn)->imm());
512 }
513
514 using Cond = CodeEmitter::Condition;
515
TestFoldCond(Cond input_cond,Cond expected_new_cond,uint16_t expected_flags_mask)516 void TestFoldCond(Cond input_cond, Cond expected_new_cond, uint16_t expected_flags_mask) {
517 Arena arena;
518 MachineIR machine_ir(&arena);
519
520 auto* bb = machine_ir.NewBasicBlock();
521 MachineIRBuilder builder(&machine_ir);
522
523 builder.StartBasicBlock(bb);
524 builder.Gen<PseudoWriteFlags>(kMachineRegRAX, kMachineRegFLAGS);
525 builder.Gen<PseudoCondBranch>(input_cond, nullptr, nullptr, kMachineRegFLAGS);
526
527 MachineReg flags_src = (*bb->insn_list().begin())->RegAt(0);
528
529 FoldWriteFlags(&machine_ir);
530
531 EXPECT_EQ(bb->insn_list().size(), 2UL);
532
533 auto insn_it = bb->insn_list().begin();
534 const auto* insn = AsMachineInsnX86_64(*insn_it);
535 EXPECT_EQ(insn->opcode(), kMachineOpTestwRegImm);
536 EXPECT_EQ(flags_src, insn->RegAt(0));
537 EXPECT_EQ(expected_flags_mask, static_cast<uint16_t>(insn->imm()));
538 MachineReg flags = insn->RegAt(1);
539
540 const auto* branch = static_cast<const PseudoCondBranch*>(*(++insn_it));
541 EXPECT_EQ(branch->opcode(), kMachineOpPseudoCondBranch);
542 EXPECT_EQ(flags, branch->RegAt(0));
543 EXPECT_EQ(expected_new_cond, branch->cond());
544 }
545
TEST(InsnFoldingTest,FoldWriteFlags)546 TEST(InsnFoldingTest, FoldWriteFlags) {
547 TestFoldCond(Cond::kEqual, Cond::kNotEqual, PseudoWriteFlags::Flags::kZero);
548 TestFoldCond(Cond::kNotEqual, Cond::kEqual, PseudoWriteFlags::Flags::kZero);
549 TestFoldCond(Cond::kCarry, Cond::kNotEqual, PseudoWriteFlags::Flags::kCarry);
550 TestFoldCond(Cond::kNotCarry, Cond::kEqual, PseudoWriteFlags::Flags::kCarry);
551 TestFoldCond(Cond::kNegative, Cond::kNotEqual, PseudoWriteFlags::Flags::kNegative);
552 TestFoldCond(Cond::kNotSign, Cond::kEqual, PseudoWriteFlags::Flags::kNegative);
553 TestFoldCond(Cond::kOverflow, Cond::kNotEqual, PseudoWriteFlags::Flags::kOverflow);
554 TestFoldCond(Cond::kNoOverflow, Cond::kEqual, PseudoWriteFlags::Flags::kOverflow);
555 }
556
557 } // namespace
558
559 } // namespace berberis::x86_64
560