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 #ifndef BERBERIS_BACKEND_X86_64_MEM_OPERAND_H_
18 #define BERBERIS_BACKEND_X86_64_MEM_OPERAND_H_
19 
20 #include <cstdint>
21 
22 #include "berberis/backend/x86_64/machine_ir.h"
23 #include "berberis/backend/x86_64/machine_ir_builder.h"
24 #include "berberis/base/logging.h"
25 
26 namespace berberis {
27 
28 namespace x86_64 {
29 
30 class MemOperand {
31  public:
32   enum AddrMode { kAddrModeInvalid, kAddrModeBaseDisp, kAddrModeIndexDisp, kAddrModeBaseIndexDisp };
33 
MemOperand()34   MemOperand() : addr_mode_(kAddrModeInvalid), scale_(MachineMemOperandScale::kOne), disp_(0) {}
35 
MakeBaseDisp(MachineReg base,int32_t disp)36   static MemOperand MakeBaseDisp(MachineReg base, int32_t disp) {
37     return MemOperand(
38         kAddrModeBaseDisp, base, kInvalidMachineReg, MachineMemOperandScale::kOne, disp);
39   }
40 
41   template <MachineMemOperandScale scale>
MakeIndexDisp(MachineReg index,int32_t disp)42   static MemOperand MakeIndexDisp(MachineReg index, int32_t disp) {
43     // We do not accept kOne here.  BaseDisp has
44     // better encoding than IndexDisp with kOne.
45     // Also, we do not want to have two ways to express reg + disp.
46     static_assert(scale != MachineMemOperandScale::kOne, "ScaleOne not allowed");
47     return MemOperand(kAddrModeIndexDisp, kInvalidMachineReg, index, scale, disp);
48   }
49 
50   template <MachineMemOperandScale scale>
MakeBaseIndexDisp(MachineReg base,MachineReg index,int32_t disp)51   static MemOperand MakeBaseIndexDisp(MachineReg base, MachineReg index, int32_t disp) {
52     return MemOperand(kAddrModeBaseIndexDisp, base, index, scale, disp);
53   }
54 
addr_mode()55   AddrMode addr_mode() const { return addr_mode_; }
56 
base()57   MachineReg base() const {
58     CHECK(addr_mode_ == kAddrModeBaseDisp || addr_mode_ == kAddrModeBaseIndexDisp);
59     return base_;
60   }
61 
index()62   MachineReg index() const {
63     CHECK(addr_mode_ == kAddrModeIndexDisp || addr_mode_ == kAddrModeBaseIndexDisp);
64     return index_;
65   }
66 
scale()67   MachineMemOperandScale scale() const {
68     CHECK(addr_mode_ == kAddrModeIndexDisp || addr_mode_ == kAddrModeBaseIndexDisp);
69     return scale_;
70   }
71 
disp()72   int32_t disp() const {
73     CHECK_NE(addr_mode_, kAddrModeInvalid);
74     return disp_;
75   }
76 
IsValid()77   bool IsValid() const { return addr_mode_ != kAddrModeInvalid; }
78 
79  private:
80   // We keep this general constructor private. Users must call
81   // MakeBaseDisp, MakeIndexDisp etc. This way, it's obvious to callers
82   // what addressing mode is being requested because the method names
83   // contain addressing modes.
MemOperand(AddrMode addr_mode,MachineReg base,MachineReg index,MachineMemOperandScale scale,int32_t disp)84   MemOperand(AddrMode addr_mode,
85              MachineReg base,
86              MachineReg index,
87              MachineMemOperandScale scale,
88              int32_t disp)
89       : addr_mode_(addr_mode), base_(base), index_(index), scale_(scale), disp_(disp) {}
90 
91   const AddrMode addr_mode_;
92   const MachineReg base_;
93   const MachineReg index_;
94   const MachineMemOperandScale scale_;
95   // The hardware sign-extends disp to 64-bit.
96   const int32_t disp_;
97 };
98 
99 template <typename MachineInsnMemInsns, typename... Args>
GenArgsMem(MachineIRBuilder * builder,const MemOperand & mem_operand,Args...args)100 void GenArgsMem(MachineIRBuilder* builder, const MemOperand& mem_operand, Args... args) {
101   switch (mem_operand.addr_mode()) {
102     case MemOperand::kAddrModeBaseDisp:
103       builder->Gen<typename MachineInsnMemInsns::BaseDisp>(
104           args..., mem_operand.base(), mem_operand.disp());
105       break;
106     case MemOperand::kAddrModeIndexDisp:
107       builder->Gen<typename MachineInsnMemInsns::IndexDisp>(
108           args..., mem_operand.index(), mem_operand.scale(), mem_operand.disp());
109       break;
110     case MemOperand::kAddrModeBaseIndexDisp:
111       builder->Gen<typename MachineInsnMemInsns::BaseIndexDisp>(args...,
112                                                                 mem_operand.base(),
113                                                                 mem_operand.index(),
114                                                                 mem_operand.scale(),
115                                                                 mem_operand.disp());
116       break;
117     default:
118       FATAL("Impossible addressing mode");
119   }
120 }
121 
122 template <typename MachineInsnMemInsns, typename... Args>
GenMemArgs(MachineIRBuilder * builder,const MemOperand & mem_operand,Args...args)123 void GenMemArgs(MachineIRBuilder* builder, const MemOperand& mem_operand, Args... args) {
124   switch (mem_operand.addr_mode()) {
125     case MemOperand::kAddrModeBaseDisp:
126       builder->Gen<typename MachineInsnMemInsns::BaseDisp>(
127           mem_operand.base(), mem_operand.disp(), args...);
128       break;
129     case MemOperand::kAddrModeIndexDisp:
130       builder->Gen<typename MachineInsnMemInsns::IndexDisp>(
131           mem_operand.index(), mem_operand.scale(), mem_operand.disp(), args...);
132       break;
133     case MemOperand::kAddrModeBaseIndexDisp:
134       builder->Gen<typename MachineInsnMemInsns::BaseIndexDisp>(mem_operand.base(),
135                                                                 mem_operand.index(),
136                                                                 mem_operand.scale(),
137                                                                 mem_operand.disp(),
138                                                                 args...);
139       break;
140     default:
141       FATAL("Impossible addressing mode");
142   }
143 }
144 
145 }  // namespace x86_64
146 
147 }  // namespace berberis
148 
149 #endif  // BERBERIS_BACKEND_X86_64_MEM_OPERAND_H_
150