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 "gtest/gtest.h"
18 
19 #include "berberis/backend/x86_64/local_guest_context_optimizer.h"
20 
21 #include "berberis/backend/x86_64/machine_ir.h"
22 #include "berberis/backend/x86_64/machine_ir_builder.h"
23 #include "berberis/backend/x86_64/machine_ir_check.h"
24 #include "berberis/base/arena_alloc.h"
25 #include "berberis/guest_state/guest_addr.h"
26 #include "berberis/guest_state/guest_state.h"
27 
28 namespace berberis {
29 
30 namespace {
31 
TEST(MachineIRLocalGuestContextOptimizer,RemoveReadAfterWrite)32 TEST(MachineIRLocalGuestContextOptimizer, RemoveReadAfterWrite) {
33   Arena arena;
34   x86_64::MachineIR machine_ir(&arena);
35 
36   x86_64::MachineIRBuilder builder(&machine_ir);
37 
38   auto bb = machine_ir.NewBasicBlock();
39   builder.StartBasicBlock(bb);
40   auto reg1 = machine_ir.AllocVReg();
41   auto reg2 = machine_ir.AllocVReg();
42   builder.GenPut(GetThreadStateRegOffset(0), reg1);
43   builder.GenGet(reg2, GetThreadStateRegOffset(0));
44   builder.Gen<PseudoJump>(kNullGuestAddr);
45 
46   x86_64::RemoveLocalGuestContextAccesses(&machine_ir);
47   ASSERT_EQ(x86_64::CheckMachineIR(machine_ir), x86_64::kMachineIRCheckSuccess);
48 
49   ASSERT_EQ(bb->insn_list().size(), 3UL);
50 
51   auto* store_insn = *bb->insn_list().begin();
52   ASSERT_EQ(store_insn->opcode(), kMachineOpMovqMemBaseDispReg);
53   auto disp = x86_64::AsMachineInsnX86_64(store_insn)->disp();
54   ASSERT_EQ(disp, berberis::GetThreadStateRegOffset(0));
55   auto replaced_reg = store_insn->RegAt(1);
56   ASSERT_EQ(store_insn->RegAt(0), x86_64::kMachineRegRBP);
57 
58   auto* load_copy_insn = *std::next(bb->insn_list().begin());
59   ASSERT_EQ(load_copy_insn->opcode(), kMachineOpPseudoCopy);
60   ASSERT_EQ(load_copy_insn->RegAt(0), reg2);
61   ASSERT_EQ(load_copy_insn->RegAt(1), replaced_reg);
62 }
63 
TEST(MachineIRLocalGuestContextOptimizer,RemoveReadAfterRead)64 TEST(MachineIRLocalGuestContextOptimizer, RemoveReadAfterRead) {
65   Arena arena;
66   x86_64::MachineIR machine_ir(&arena);
67 
68   x86_64::MachineIRBuilder builder(&machine_ir);
69 
70   auto bb = machine_ir.NewBasicBlock();
71   builder.StartBasicBlock(bb);
72   auto reg1 = machine_ir.AllocVReg();
73   auto reg2 = machine_ir.AllocVReg();
74   builder.GenGet(reg1, GetThreadStateRegOffset(0));
75   builder.GenGet(reg2, GetThreadStateRegOffset(0));
76   builder.Gen<PseudoJump>(kNullGuestAddr);
77 
78   x86_64::RemoveLocalGuestContextAccesses(&machine_ir);
79   ASSERT_EQ(x86_64::CheckMachineIR(machine_ir), x86_64::kMachineIRCheckSuccess);
80 
81   ASSERT_EQ(bb->insn_list().size(), 3UL);
82   auto* load_insn = *bb->insn_list().begin();
83   ASSERT_EQ(load_insn->opcode(), kMachineOpMovqRegMemBaseDisp);
84   ASSERT_EQ(x86_64::AsMachineInsnX86_64(load_insn)->disp(), berberis::GetThreadStateRegOffset(0));
85   ASSERT_EQ(load_insn->RegAt(0), reg1);
86   ASSERT_EQ(load_insn->RegAt(1), x86_64::kMachineRegRBP);
87 
88   auto* copy_insn = *std::next(bb->insn_list().begin());
89   ASSERT_EQ(copy_insn->opcode(), kMachineOpPseudoCopy);
90   ASSERT_EQ(copy_insn->RegAt(0), reg2);
91   ASSERT_EQ(copy_insn->RegAt(1), reg1);
92 }
93 
TEST(MachineIRLocalGuestContextOptimizer,RemoveWriteAfterWrite)94 TEST(MachineIRLocalGuestContextOptimizer, RemoveWriteAfterWrite) {
95   Arena arena;
96   x86_64::MachineIR machine_ir(&arena);
97 
98   x86_64::MachineIRBuilder builder(&machine_ir);
99 
100   auto bb = machine_ir.NewBasicBlock();
101   builder.StartBasicBlock(bb);
102   auto reg1 = machine_ir.AllocVReg();
103   auto reg2 = machine_ir.AllocVReg();
104   builder.GenPut(GetThreadStateRegOffset(0), reg1);
105   builder.GenPut(GetThreadStateRegOffset(0), reg2);
106   builder.Gen<PseudoJump>(kNullGuestAddr);
107 
108   x86_64::RemoveLocalGuestContextAccesses(&machine_ir);
109   ASSERT_EQ(x86_64::CheckMachineIR(machine_ir), x86_64::kMachineIRCheckSuccess);
110 
111   ASSERT_EQ(bb->insn_list().size(), 2UL);
112   auto* store_insn = *bb->insn_list().begin();
113   ASSERT_EQ(store_insn->opcode(), kMachineOpMovqMemBaseDispReg);
114   ASSERT_EQ(x86_64::AsMachineInsnX86_64(store_insn)->disp(), berberis::GetThreadStateRegOffset(0));
115   ASSERT_EQ(store_insn->RegAt(1), reg2);
116   ASSERT_EQ(store_insn->RegAt(0), x86_64::kMachineRegRBP);
117 }
118 
TEST(MachineIRLocalGuestContextOptimizer,DoNotRemoveAccessToMonitorValue)119 TEST(MachineIRLocalGuestContextOptimizer, DoNotRemoveAccessToMonitorValue) {
120   Arena arena;
121   x86_64::MachineIR machine_ir(&arena);
122 
123   x86_64::MachineIRBuilder builder(&machine_ir);
124 
125   auto bb = machine_ir.NewBasicBlock();
126   builder.StartBasicBlock(bb);
127   auto reg1 = machine_ir.AllocVReg();
128   auto reg2 = machine_ir.AllocVReg();
129   auto offset = offsetof(ProcessState, cpu.reservation_value);
130   builder.Gen<x86_64::MovqMemBaseDispReg>(x86_64::kMachineRegRBP, offset, reg1);
131   builder.Gen<x86_64::MovqMemBaseDispReg>(x86_64::kMachineRegRBP, offset, reg2);
132   builder.Gen<PseudoJump>(kNullGuestAddr);
133 
134   x86_64::RemoveLocalGuestContextAccesses(&machine_ir);
135   ASSERT_EQ(x86_64::CheckMachineIR(machine_ir), x86_64::kMachineIRCheckSuccess);
136 
137   ASSERT_EQ(bb->insn_list().size(), 3UL);
138   auto* store_insn_1 = *bb->insn_list().begin();
139   ASSERT_EQ(store_insn_1->opcode(), kMachineOpMovqMemBaseDispReg);
140   ASSERT_EQ(x86_64::AsMachineInsnX86_64(store_insn_1)->disp(), offset);
141 
142   auto* store_insn_2 = *std::next(bb->insn_list().begin());
143   ASSERT_EQ(store_insn_2->opcode(), kMachineOpMovqMemBaseDispReg);
144   ASSERT_EQ(x86_64::AsMachineInsnX86_64(store_insn_2)->disp(), offset);
145 }
146 
147 }  // namespace
148 
149 }  // namespace berberis
150