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