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