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