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 "berberis/backend/x86_64/local_guest_context_optimizer.h"
18 
19 #include <optional>
20 
21 #include "berberis/base/arena_vector.h"
22 
23 namespace berberis::x86_64 {
24 
25 namespace {
26 
27 class LocalGuestContextOptimizer {
28  public:
LocalGuestContextOptimizer(x86_64::MachineIR * machine_ir)29   explicit LocalGuestContextOptimizer(x86_64::MachineIR* machine_ir)
30       : machine_ir_(machine_ir),
31         mem_reg_map_(sizeof(CPUState), std::nullopt, machine_ir->arena()) {}
32 
33   void RemoveLocalGuestContextAccesses();
34 
35  private:
36   struct MappedRegUsage {
37     MachineReg reg;
38     std::optional<MachineInsnList::iterator> last_store;
39   };
40 
41   void ReplaceGetAndUpdateMap(const MachineInsnList::iterator insn_it);
42   void ReplacePutAndUpdateMap(MachineInsnList& insn_list, const MachineInsnList::iterator insn_it);
43 
44   MachineIR* machine_ir_;
45   ArenaVector<std::optional<MappedRegUsage>> mem_reg_map_;
46 };
47 
RemoveLocalGuestContextAccesses()48 void LocalGuestContextOptimizer::RemoveLocalGuestContextAccesses() {
49   for (auto* bb : machine_ir_->bb_list()) {
50     std::fill(mem_reg_map_.begin(), mem_reg_map_.end(), std::nullopt);
51     for (auto insn_it = bb->insn_list().begin(); insn_it != bb->insn_list().end(); insn_it++) {
52       auto* insn = AsMachineInsnX86_64(*insn_it);
53       if (insn->IsCPUStateGet()) {
54         ReplaceGetAndUpdateMap(insn_it);
55       } else if (insn->IsCPUStatePut()) {
56         ReplacePutAndUpdateMap(bb->insn_list(), insn_it);
57       }
58     }
59   }
60 }
61 
ReplaceGetAndUpdateMap(const MachineInsnList::iterator insn_it)62 void LocalGuestContextOptimizer::ReplaceGetAndUpdateMap(const MachineInsnList::iterator insn_it) {
63   auto* insn = AsMachineInsnX86_64(*insn_it);
64   auto dst = insn->RegAt(0);
65   auto disp = insn->disp();
66 
67   // We only need to keep this load instruction if this is the first access to
68   // the guest context at disp.
69   if (!mem_reg_map_[disp].has_value()) {
70     mem_reg_map_[disp] = {dst, {}};
71     return;
72   }
73 
74   auto copy_size = insn->opcode() == kMachineOpMovdqaXRegMemBaseDisp ? 16 : 8;
75   *insn_it = machine_ir_->NewInsn<PseudoCopy>(dst, mem_reg_map_[disp].value().reg, copy_size);
76 }
77 
ReplacePutAndUpdateMap(MachineInsnList & insn_list,const MachineInsnList::iterator insn_it)78 void LocalGuestContextOptimizer::ReplacePutAndUpdateMap(MachineInsnList& insn_list,
79                                                         const MachineInsnList::iterator insn_it) {
80   auto* insn = AsMachineInsnX86_64(*insn_it);
81   auto src = insn->RegAt(1);
82   auto disp = insn->disp();
83 
84   if (mem_reg_map_[disp].has_value() && mem_reg_map_[disp].value().last_store.has_value()) {
85     // Remove the last store instruction.
86     auto last_store_it = mem_reg_map_[disp].value().last_store.value();
87     insn_list.erase(last_store_it);
88   }
89 
90   mem_reg_map_[disp] = {src, {insn_it}};
91 }
92 
93 }  // namespace
94 
RemoveLocalGuestContextAccesses(x86_64::MachineIR * machine_ir)95 void RemoveLocalGuestContextAccesses(x86_64::MachineIR* machine_ir) {
96   LocalGuestContextOptimizer optimizer(machine_ir);
97   optimizer.RemoveLocalGuestContextAccesses();
98 }
99 
100 }  // namespace berberis::x86_64
101