1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | // |
6 | // This file declares the types that constitute the interface between the |
7 | // code generator (CodeGen class) and the rest of the JIT. |
8 | // |
9 | // RegState |
10 | // |
11 | // CodeGenInterface includes only the public methods that are called by |
12 | // the Compiler. |
13 | // |
14 | // CodeGenContext contains the shared context between the code generator |
15 | // and other phases of the JIT, especially the register allocator and |
16 | // GC encoder. It is distinct from CodeGenInterface so that it can be |
17 | // included in the Compiler object, and avoid an extra indirection when |
18 | // accessed from members of Compiler. |
19 | // |
20 | |
21 | #ifndef _CODEGEN_INTERFACE_H_ |
22 | #define _CODEGEN_INTERFACE_H_ |
23 | |
24 | #include "regset.h" |
25 | #include "jitgcinfo.h" |
26 | #include "treelifeupdater.h" |
27 | |
28 | // Forward reference types |
29 | |
30 | class CodeGenInterface; |
31 | class emitter; |
32 | |
33 | // Small helper types |
34 | |
35 | //-------------------- Register selection --------------------------------- |
36 | |
37 | struct RegState |
38 | { |
39 | regMaskTP rsCalleeRegArgMaskLiveIn; // mask of register arguments (live on entry to method) |
40 | unsigned rsCalleeRegArgCount; // total number of incoming register arguments of this kind (int or float) |
41 | bool rsIsFloat; // true for float argument registers, false for integer argument registers |
42 | }; |
43 | |
44 | //-------------------- CodeGenInterface --------------------------------- |
45 | // interface to hide the full CodeGen implementation from rest of Compiler |
46 | |
47 | CodeGenInterface* getCodeGenerator(Compiler* comp); |
48 | |
49 | class CodeGenInterface |
50 | { |
51 | friend class emitter; |
52 | |
53 | public: |
54 | CodeGenInterface(Compiler* theCompiler); |
55 | virtual void genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) = 0; |
56 | |
57 | // genSpillVar is called by compUpdateLifeVar. |
58 | // TODO-Cleanup: We should handle the spill directly in CodeGen, rather than |
59 | // calling it from compUpdateLifeVar. Then this can be non-virtual. |
60 | |
61 | virtual void genSpillVar(GenTree* tree) = 0; |
62 | |
63 | //------------------------------------------------------------------------- |
64 | // The following property indicates whether to align loops. |
65 | // (Used to avoid effects of loop alignment when diagnosing perf issues.) |
66 | __declspec(property(get = doAlignLoops, put = setAlignLoops)) bool genAlignLoops; |
67 | bool doAlignLoops() |
68 | { |
69 | return m_genAlignLoops; |
70 | } |
71 | void setAlignLoops(bool value) |
72 | { |
73 | m_genAlignLoops = value; |
74 | } |
75 | |
76 | // TODO-Cleanup: Abstract out the part of this that finds the addressing mode, and |
77 | // move it to Lower |
78 | virtual bool genCreateAddrMode(GenTree* addr, |
79 | bool fold, |
80 | bool* revPtr, |
81 | GenTree** rv1Ptr, |
82 | GenTree** rv2Ptr, |
83 | #if SCALED_ADDR_MODES |
84 | unsigned* mulPtr, |
85 | #endif // SCALED_ADDR_MODES |
86 | ssize_t* cnsPtr) = 0; |
87 | |
88 | GCInfo gcInfo; |
89 | |
90 | RegSet regSet; |
91 | RegState intRegState; |
92 | RegState floatRegState; |
93 | |
94 | protected: |
95 | Compiler* compiler; |
96 | bool m_genAlignLoops; |
97 | |
98 | private: |
99 | #if defined(_TARGET_XARCH_) |
100 | static const insFlags instInfo[INS_count]; |
101 | #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) |
102 | static const BYTE instInfo[INS_count]; |
103 | #else |
104 | #error Unsupported target architecture |
105 | #endif |
106 | |
107 | #define INST_FP 0x01 // is it a FP instruction? |
108 | public: |
109 | static bool instIsFP(instruction ins); |
110 | |
111 | //------------------------------------------------------------------------- |
112 | // Liveness-related fields & methods |
113 | public: |
114 | void genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bool isDying DEBUGARG(GenTree* tree)); |
115 | void genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree); |
116 | |
117 | protected: |
118 | #ifdef DEBUG |
119 | VARSET_TP genTempOldLife; |
120 | bool genTempLiveChg; |
121 | #endif |
122 | |
123 | VARSET_TP genLastLiveSet; // A one element map (genLastLiveSet-> genLastLiveMask) |
124 | regMaskTP genLastLiveMask; // these two are used in genLiveMask |
125 | |
126 | regMaskTP genGetRegMask(const LclVarDsc* varDsc); |
127 | regMaskTP genGetRegMask(GenTree* tree); |
128 | |
129 | void genUpdateLife(GenTree* tree); |
130 | void genUpdateLife(VARSET_VALARG_TP newLife); |
131 | |
132 | TreeLifeUpdater<true>* treeLifeUpdater; |
133 | |
134 | public: |
135 | bool genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf); |
136 | bool genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal); |
137 | CorInfoHelpFunc genWriteBarrierHelperForWriteBarrierForm(GenTree* tgt, GCInfo::WriteBarrierForm wbf); |
138 | |
139 | // The following property indicates whether the current method sets up |
140 | // an explicit stack frame or not. |
141 | private: |
142 | PhasedVar<bool> m_cgFramePointerUsed; |
143 | |
144 | public: |
145 | bool isFramePointerUsed() const |
146 | { |
147 | return m_cgFramePointerUsed; |
148 | } |
149 | void setFramePointerUsed(bool value) |
150 | { |
151 | m_cgFramePointerUsed = value; |
152 | } |
153 | void resetFramePointerUsedWritePhase() |
154 | { |
155 | m_cgFramePointerUsed.ResetWritePhase(); |
156 | } |
157 | |
158 | // The following property indicates whether the current method requires |
159 | // an explicit frame. Does not prohibit double alignment of the stack. |
160 | private: |
161 | PhasedVar<bool> m_cgFrameRequired; |
162 | |
163 | public: |
164 | bool isFrameRequired() const |
165 | { |
166 | return m_cgFrameRequired; |
167 | } |
168 | void setFrameRequired(bool value) |
169 | { |
170 | m_cgFrameRequired = value; |
171 | } |
172 | |
173 | public: |
174 | int genCallerSPtoFPdelta(); |
175 | int genCallerSPtoInitialSPdelta(); |
176 | int genSPtoFPdelta(); |
177 | int genTotalFrameSize(); |
178 | |
179 | regNumber genGetThisArgReg(GenTreeCall* call) const; |
180 | |
181 | #ifdef _TARGET_XARCH_ |
182 | #ifdef _TARGET_AMD64_ |
183 | // There are no reloc hints on x86 |
184 | unsigned short genAddrRelocTypeHint(size_t addr); |
185 | #endif |
186 | bool genDataIndirAddrCanBeEncodedAsPCRelOffset(size_t addr); |
187 | bool genCodeIndirAddrCanBeEncodedAsPCRelOffset(size_t addr); |
188 | bool genCodeIndirAddrCanBeEncodedAsZeroRelOffset(size_t addr); |
189 | bool genCodeIndirAddrNeedsReloc(size_t addr); |
190 | bool genCodeAddrNeedsReloc(size_t addr); |
191 | #endif |
192 | |
193 | // If both isFramePointerRequired() and isFrameRequired() are false, the method is eligible |
194 | // for Frame-Pointer-Omission (FPO). |
195 | |
196 | // The following property indicates whether the current method requires |
197 | // an explicit stack frame, and all arguments and locals to be |
198 | // accessible relative to the Frame Pointer. Prohibits double alignment |
199 | // of the stack. |
200 | private: |
201 | PhasedVar<bool> m_cgFramePointerRequired; |
202 | |
203 | public: |
204 | bool isFramePointerRequired() const |
205 | { |
206 | return m_cgFramePointerRequired; |
207 | } |
208 | |
209 | void setFramePointerRequired(bool value) |
210 | { |
211 | m_cgFramePointerRequired = value; |
212 | } |
213 | |
214 | //------------------------------------------------------------------------ |
215 | // resetWritePhaseForFramePointerRequired: Return m_cgFramePointerRequired into the write phase. |
216 | // It is used only before the first phase, that locks this value, currently it is LSRA. |
217 | // Use it if you want to skip checks that set this value to true if the value is already true. |
218 | void resetWritePhaseForFramePointerRequired() |
219 | { |
220 | m_cgFramePointerRequired.ResetWritePhase(); |
221 | } |
222 | |
223 | void setFramePointerRequiredEH(bool value); |
224 | |
225 | void setFramePointerRequiredGCInfo(bool value) |
226 | { |
227 | #ifdef JIT32_GCENCODER |
228 | m_cgFramePointerRequired = value; |
229 | #endif |
230 | } |
231 | |
232 | #if DOUBLE_ALIGN |
233 | // The following property indicates whether we going to double-align the frame. |
234 | // Arguments are accessed relative to the Frame Pointer (EBP), and |
235 | // locals are accessed relative to the Stack Pointer (ESP). |
236 | public: |
237 | bool doDoubleAlign() const |
238 | { |
239 | return m_cgDoubleAlign; |
240 | } |
241 | void setDoubleAlign(bool value) |
242 | { |
243 | m_cgDoubleAlign = value; |
244 | } |
245 | bool doubleAlignOrFramePointerUsed() const |
246 | { |
247 | return isFramePointerUsed() || doDoubleAlign(); |
248 | } |
249 | |
250 | private: |
251 | bool m_cgDoubleAlign; |
252 | #else // !DOUBLE_ALIGN |
253 | |
254 | public: |
255 | bool doubleAlignOrFramePointerUsed() const |
256 | { |
257 | return isFramePointerUsed(); |
258 | } |
259 | |
260 | #endif // !DOUBLE_ALIGN |
261 | |
262 | #ifdef DEBUG |
263 | // The following is used to make sure the value of 'genInterruptible' isn't |
264 | // changed after it's been used by any logic that depends on its value. |
265 | public: |
266 | bool isGCTypeFixed() |
267 | { |
268 | return genInterruptibleUsed; |
269 | } |
270 | |
271 | protected: |
272 | bool genInterruptibleUsed; |
273 | #endif |
274 | |
275 | public: |
276 | unsigned InferStructOpSizeAlign(GenTree* op, unsigned* alignmentWB); |
277 | unsigned InferOpSizeAlign(GenTree* op, unsigned* alignmentWB); |
278 | |
279 | void genMarkTreeInReg(GenTree* tree, regNumber reg); |
280 | |
281 | // Methods to abstract target information |
282 | |
283 | bool validImmForInstr(instruction ins, target_ssize_t val, insFlags flags = INS_FLAGS_DONT_CARE); |
284 | bool validDispForLdSt(target_ssize_t disp, var_types type); |
285 | bool validImmForAdd(target_ssize_t imm, insFlags flags); |
286 | bool validImmForAlu(target_ssize_t imm); |
287 | bool validImmForMov(target_ssize_t imm); |
288 | bool validImmForBL(ssize_t addr); |
289 | |
290 | instruction ins_Load(var_types srcType, bool aligned = false); |
291 | instruction ins_Store(var_types dstType, bool aligned = false); |
292 | static instruction ins_FloatLoad(var_types type = TYP_DOUBLE); |
293 | |
294 | // Methods for spilling - used by RegSet |
295 | void spillReg(var_types type, TempDsc* tmp, regNumber reg); |
296 | void reloadReg(var_types type, TempDsc* tmp, regNumber reg); |
297 | |
298 | // The following method is used by xarch emitter for handling contained tree temps. |
299 | TempDsc* getSpillTempDsc(GenTree* tree); |
300 | |
301 | public: |
302 | emitter* getEmitter() |
303 | { |
304 | return m_cgEmitter; |
305 | } |
306 | |
307 | protected: |
308 | emitter* m_cgEmitter; |
309 | |
310 | #ifdef LATE_DISASM |
311 | public: |
312 | DisAssembler& getDisAssembler() |
313 | { |
314 | return m_cgDisAsm; |
315 | } |
316 | |
317 | protected: |
318 | DisAssembler m_cgDisAsm; |
319 | #endif // LATE_DISASM |
320 | |
321 | public: |
322 | #ifdef DEBUG |
323 | void setVerbose(bool value) |
324 | { |
325 | verbose = value; |
326 | } |
327 | bool verbose; |
328 | #endif // DEBUG |
329 | |
330 | // The following is set to true if we've determined that the current method |
331 | // is to be fully interruptible. |
332 | // |
333 | public: |
334 | __declspec(property(get = getInterruptible, put = setInterruptible)) bool genInterruptible; |
335 | bool getInterruptible() |
336 | { |
337 | return m_cgInterruptible; |
338 | } |
339 | void setInterruptible(bool value) |
340 | { |
341 | m_cgInterruptible = value; |
342 | } |
343 | |
344 | #ifdef _TARGET_ARMARCH_ |
345 | __declspec(property(get = getHasTailCalls, put = setHasTailCalls)) bool hasTailCalls; |
346 | bool getHasTailCalls() |
347 | { |
348 | return m_cgHasTailCalls; |
349 | } |
350 | void setHasTailCalls(bool value) |
351 | { |
352 | m_cgHasTailCalls = value; |
353 | } |
354 | #endif // _TARGET_ARMARCH_ |
355 | |
356 | private: |
357 | bool m_cgInterruptible; |
358 | #ifdef _TARGET_ARMARCH_ |
359 | bool m_cgHasTailCalls; |
360 | #endif // _TARGET_ARMARCH_ |
361 | |
362 | // The following will be set to true if we've determined that we need to |
363 | // generate a full-blown pointer register map for the current method. |
364 | // Currently it is equal to (genInterruptible || !isFramePointerUsed()) |
365 | // (i.e. We generate the full-blown map for EBP-less methods and |
366 | // for fully interruptible methods) |
367 | // |
368 | public: |
369 | __declspec(property(get = doFullPtrRegMap, put = setFullPtrRegMap)) bool genFullPtrRegMap; |
370 | bool doFullPtrRegMap() |
371 | { |
372 | return m_cgFullPtrRegMap; |
373 | } |
374 | void setFullPtrRegMap(bool value) |
375 | { |
376 | m_cgFullPtrRegMap = value; |
377 | } |
378 | |
379 | private: |
380 | bool m_cgFullPtrRegMap; |
381 | |
382 | public: |
383 | virtual void siUpdate() = 0; |
384 | |
385 | #ifdef LATE_DISASM |
386 | public: |
387 | virtual const char* siRegVarName(size_t offs, size_t size, unsigned reg) = 0; |
388 | |
389 | virtual const char* siStackVarName(size_t offs, size_t size, unsigned reg, unsigned stkOffs) = 0; |
390 | #endif // LATE_DISASM |
391 | }; |
392 | |
393 | #endif // _CODEGEN_INTERFACE_H_ |
394 | |