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 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX Instruction XX |
9 | XX XX |
10 | XX The interface to generate a machine-instruction. XX |
11 | XX XX |
12 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
13 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
14 | */ |
15 | |
16 | #include "jitpch.h" |
17 | #ifdef _MSC_VER |
18 | #pragma hdrstop |
19 | #endif |
20 | |
21 | #include "codegen.h" |
22 | #include "instr.h" |
23 | #include "emit.h" |
24 | |
25 | /*****************************************************************************/ |
26 | #ifdef DEBUG |
27 | |
28 | /***************************************************************************** |
29 | * |
30 | * Returns the string representation of the given CPU instruction. |
31 | */ |
32 | |
33 | const char* CodeGen::genInsName(instruction ins) |
34 | { |
35 | // clang-format off |
36 | static |
37 | const char * const insNames[] = |
38 | { |
39 | #if defined(_TARGET_XARCH_) |
40 | #define INST0(id, nm, um, mr, flags) nm, |
41 | #define INST1(id, nm, um, mr, flags) nm, |
42 | #define INST2(id, nm, um, mr, mi, flags) nm, |
43 | #define INST3(id, nm, um, mr, mi, rm, flags) nm, |
44 | #define INST4(id, nm, um, mr, mi, rm, a4, flags) nm, |
45 | #define INST5(id, nm, um, mr, mi, rm, a4, rr, flags) nm, |
46 | #include "instrs.h" |
47 | |
48 | #elif defined(_TARGET_ARM_) |
49 | #define INST1(id, nm, fp, ldst, fmt, e1 ) nm, |
50 | #define INST2(id, nm, fp, ldst, fmt, e1, e2 ) nm, |
51 | #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3 ) nm, |
52 | #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4 ) nm, |
53 | #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5 ) nm, |
54 | #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6 ) nm, |
55 | #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8 ) nm, |
56 | #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9 ) nm, |
57 | #include "instrs.h" |
58 | |
59 | #elif defined(_TARGET_ARM64_) |
60 | #define INST1(id, nm, fp, ldst, fmt, e1 ) nm, |
61 | #define INST2(id, nm, fp, ldst, fmt, e1, e2 ) nm, |
62 | #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3 ) nm, |
63 | #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4 ) nm, |
64 | #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5 ) nm, |
65 | #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6 ) nm, |
66 | #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9 ) nm, |
67 | #include "instrs.h" |
68 | |
69 | #else |
70 | #error "Unknown _TARGET_" |
71 | #endif |
72 | }; |
73 | // clang-format on |
74 | |
75 | assert((unsigned)ins < _countof(insNames)); |
76 | assert(insNames[ins] != nullptr); |
77 | |
78 | return insNames[ins]; |
79 | } |
80 | |
81 | void __cdecl CodeGen::instDisp(instruction ins, bool noNL, const char* fmt, ...) |
82 | { |
83 | if (compiler->opts.dspCode) |
84 | { |
85 | /* Display the instruction offset within the emit block */ |
86 | |
87 | // printf("[%08X:%04X]", getEmitter().emitCodeCurBlock(), getEmitter().emitCodeOffsInBlock()); |
88 | |
89 | /* Display the FP stack depth (before the instruction is executed) */ |
90 | |
91 | // printf("[FP=%02u] ", genGetFPstkLevel()); |
92 | |
93 | /* Display the instruction mnemonic */ |
94 | printf(" " ); |
95 | |
96 | printf(" %-8s" , genInsName(ins)); |
97 | |
98 | if (fmt) |
99 | { |
100 | va_list args; |
101 | va_start(args, fmt); |
102 | vprintf(fmt, args); |
103 | va_end(args); |
104 | } |
105 | |
106 | if (!noNL) |
107 | { |
108 | printf("\n" ); |
109 | } |
110 | } |
111 | } |
112 | |
113 | /*****************************************************************************/ |
114 | #endif // DEBUG |
115 | /*****************************************************************************/ |
116 | |
117 | void CodeGen::instInit() |
118 | { |
119 | } |
120 | |
121 | /***************************************************************************** |
122 | * |
123 | * Return the size string (e.g. "word ptr") appropriate for the given size. |
124 | */ |
125 | |
126 | #ifdef DEBUG |
127 | |
128 | const char* CodeGen::genSizeStr(emitAttr attr) |
129 | { |
130 | // clang-format off |
131 | static |
132 | const char * const sizes[] = |
133 | { |
134 | "" , |
135 | "byte ptr " , |
136 | "word ptr " , |
137 | nullptr, |
138 | "dword ptr " , |
139 | nullptr, |
140 | nullptr, |
141 | nullptr, |
142 | "qword ptr " , |
143 | nullptr, |
144 | nullptr, |
145 | nullptr, |
146 | nullptr, |
147 | nullptr, |
148 | nullptr, |
149 | nullptr, |
150 | "xmmword ptr " , |
151 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
152 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
153 | "ymmword ptr" |
154 | }; |
155 | // clang-format on |
156 | |
157 | unsigned size = EA_SIZE(attr); |
158 | |
159 | assert(size == 0 || size == 1 || size == 2 || size == 4 || size == 8 || size == 16 || size == 32); |
160 | |
161 | if (EA_ATTR(size) == attr) |
162 | { |
163 | return sizes[size]; |
164 | } |
165 | else if (attr == EA_GCREF) |
166 | { |
167 | return "gword ptr " ; |
168 | } |
169 | else if (attr == EA_BYREF) |
170 | { |
171 | return "bword ptr " ; |
172 | } |
173 | else if (EA_IS_DSP_RELOC(attr)) |
174 | { |
175 | return "rword ptr " ; |
176 | } |
177 | else |
178 | { |
179 | assert(!"Unexpected" ); |
180 | return "unknw ptr " ; |
181 | } |
182 | } |
183 | |
184 | #endif |
185 | |
186 | /***************************************************************************** |
187 | * |
188 | * Generate an instruction. |
189 | */ |
190 | |
191 | void CodeGen::instGen(instruction ins) |
192 | { |
193 | |
194 | getEmitter()->emitIns(ins); |
195 | |
196 | #ifdef _TARGET_XARCH_ |
197 | // A workaround necessitated by limitations of emitter |
198 | // if we are scheduled to insert a nop here, we have to delay it |
199 | // hopefully we have not missed any other prefix instructions or places |
200 | // they could be inserted |
201 | if (ins == INS_lock && getEmitter()->emitNextNop == 0) |
202 | { |
203 | getEmitter()->emitNextNop = 1; |
204 | } |
205 | #endif |
206 | } |
207 | |
208 | /***************************************************************************** |
209 | * |
210 | * Returns non-zero if the given CPU instruction is a floating-point ins. |
211 | */ |
212 | |
213 | // static inline |
214 | bool CodeGenInterface::instIsFP(instruction ins) |
215 | { |
216 | assert((unsigned)ins < _countof(instInfo)); |
217 | |
218 | #ifdef _TARGET_XARCH_ |
219 | return (instInfo[ins] & INS_FLAGS_x87Instr) != 0; |
220 | #else |
221 | return (instInfo[ins] & INST_FP) != 0; |
222 | #endif |
223 | } |
224 | |
225 | #ifdef _TARGET_XARCH_ |
226 | /***************************************************************************** |
227 | * |
228 | * Generate a multi-byte NOP instruction. |
229 | */ |
230 | |
231 | void CodeGen::instNop(unsigned size) |
232 | { |
233 | assert(size <= 15); |
234 | getEmitter()->emitIns_Nop(size); |
235 | } |
236 | #endif |
237 | |
238 | /***************************************************************************** |
239 | * |
240 | * Generate a jump instruction. |
241 | */ |
242 | |
243 | void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock) |
244 | { |
245 | #if !FEATURE_FIXED_OUT_ARGS |
246 | // On the x86 we are pushing (and changing the stack level), but on x64 and other archs we have |
247 | // a fixed outgoing args area that we store into and we never change the stack level when calling methods. |
248 | // |
249 | // Thus only on x86 do we need to assert that the stack level at the target block matches the current stack level. |
250 | // |
251 | CLANG_FORMAT_COMMENT_ANCHOR; |
252 | |
253 | #ifdef UNIX_X86_ABI |
254 | // bbTgtStkDepth is a (pure) argument count (stack alignment padding should be excluded). |
255 | assert((tgtBlock->bbTgtStkDepth * sizeof(int) == (genStackLevel - curNestedAlignment)) || isFramePointerUsed()); |
256 | #else |
257 | assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed()); |
258 | #endif |
259 | #endif // !FEATURE_FIXED_OUT_ARGS |
260 | |
261 | getEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock); |
262 | } |
263 | |
264 | /***************************************************************************** |
265 | * |
266 | * Generate a set instruction. |
267 | */ |
268 | |
269 | void CodeGen::inst_SET(emitJumpKind condition, regNumber reg) |
270 | { |
271 | #ifdef _TARGET_XARCH_ |
272 | instruction ins; |
273 | |
274 | /* Convert the condition to an instruction opcode */ |
275 | |
276 | switch (condition) |
277 | { |
278 | case EJ_js: |
279 | ins = INS_sets; |
280 | break; |
281 | case EJ_jns: |
282 | ins = INS_setns; |
283 | break; |
284 | case EJ_je: |
285 | ins = INS_sete; |
286 | break; |
287 | case EJ_jne: |
288 | ins = INS_setne; |
289 | break; |
290 | |
291 | case EJ_jl: |
292 | ins = INS_setl; |
293 | break; |
294 | case EJ_jle: |
295 | ins = INS_setle; |
296 | break; |
297 | case EJ_jge: |
298 | ins = INS_setge; |
299 | break; |
300 | case EJ_jg: |
301 | ins = INS_setg; |
302 | break; |
303 | |
304 | case EJ_jb: |
305 | ins = INS_setb; |
306 | break; |
307 | case EJ_jbe: |
308 | ins = INS_setbe; |
309 | break; |
310 | case EJ_jae: |
311 | ins = INS_setae; |
312 | break; |
313 | case EJ_ja: |
314 | ins = INS_seta; |
315 | break; |
316 | |
317 | case EJ_jpe: |
318 | ins = INS_setpe; |
319 | break; |
320 | case EJ_jpo: |
321 | ins = INS_setpo; |
322 | break; |
323 | |
324 | default: |
325 | NO_WAY("unexpected condition type" ); |
326 | return; |
327 | } |
328 | |
329 | assert(genRegMask(reg) & RBM_BYTE_REGS); |
330 | |
331 | // These instructions only write the low byte of 'reg' |
332 | getEmitter()->emitIns_R(ins, EA_1BYTE, reg); |
333 | #elif defined(_TARGET_ARM64_) |
334 | insCond cond; |
335 | /* Convert the condition to an insCond value */ |
336 | switch (condition) |
337 | { |
338 | case EJ_eq: |
339 | cond = INS_COND_EQ; |
340 | break; |
341 | case EJ_ne: |
342 | cond = INS_COND_NE; |
343 | break; |
344 | case EJ_hs: |
345 | cond = INS_COND_HS; |
346 | break; |
347 | case EJ_lo: |
348 | cond = INS_COND_LO; |
349 | break; |
350 | |
351 | case EJ_mi: |
352 | cond = INS_COND_MI; |
353 | break; |
354 | case EJ_pl: |
355 | cond = INS_COND_PL; |
356 | break; |
357 | case EJ_vs: |
358 | cond = INS_COND_VS; |
359 | break; |
360 | case EJ_vc: |
361 | cond = INS_COND_VC; |
362 | break; |
363 | |
364 | case EJ_hi: |
365 | cond = INS_COND_HI; |
366 | break; |
367 | case EJ_ls: |
368 | cond = INS_COND_LS; |
369 | break; |
370 | case EJ_ge: |
371 | cond = INS_COND_GE; |
372 | break; |
373 | case EJ_lt: |
374 | cond = INS_COND_LT; |
375 | break; |
376 | |
377 | case EJ_gt: |
378 | cond = INS_COND_GT; |
379 | break; |
380 | case EJ_le: |
381 | cond = INS_COND_LE; |
382 | break; |
383 | |
384 | default: |
385 | NO_WAY("unexpected condition type" ); |
386 | return; |
387 | } |
388 | getEmitter()->emitIns_R_COND(INS_cset, EA_8BYTE, reg, cond); |
389 | #else |
390 | NYI("inst_SET" ); |
391 | #endif |
392 | } |
393 | |
394 | /***************************************************************************** |
395 | * |
396 | * Generate a "op reg" instruction. |
397 | */ |
398 | |
399 | void CodeGen::inst_RV(instruction ins, regNumber reg, var_types type, emitAttr size) |
400 | { |
401 | if (size == EA_UNKNOWN) |
402 | { |
403 | size = emitActualTypeSize(type); |
404 | } |
405 | |
406 | getEmitter()->emitIns_R(ins, size, reg); |
407 | } |
408 | |
409 | /***************************************************************************** |
410 | * |
411 | * Generate a "op reg1, reg2" instruction. |
412 | */ |
413 | |
414 | void CodeGen::inst_RV_RV(instruction ins, |
415 | regNumber reg1, |
416 | regNumber reg2, |
417 | var_types type, |
418 | emitAttr size, |
419 | insFlags flags /* = INS_FLAGS_DONT_CARE */) |
420 | { |
421 | if (size == EA_UNKNOWN) |
422 | { |
423 | size = emitActualTypeSize(type); |
424 | } |
425 | |
426 | #ifdef _TARGET_ARM_ |
427 | getEmitter()->emitIns_R_R(ins, size, reg1, reg2, flags); |
428 | #else |
429 | getEmitter()->emitIns_R_R(ins, size, reg1, reg2); |
430 | #endif |
431 | } |
432 | |
433 | /***************************************************************************** |
434 | * |
435 | * Generate a "op reg1, reg2, reg3" instruction. |
436 | */ |
437 | |
438 | void CodeGen::inst_RV_RV_RV(instruction ins, |
439 | regNumber reg1, |
440 | regNumber reg2, |
441 | regNumber reg3, |
442 | emitAttr size, |
443 | insFlags flags /* = INS_FLAGS_DONT_CARE */) |
444 | { |
445 | #ifdef _TARGET_ARM_ |
446 | getEmitter()->emitIns_R_R_R(ins, size, reg1, reg2, reg3, flags); |
447 | #elif defined(_TARGET_XARCH_) |
448 | getEmitter()->emitIns_R_R_R(ins, size, reg1, reg2, reg3); |
449 | #else |
450 | NYI("inst_RV_RV_RV" ); |
451 | #endif |
452 | } |
453 | /***************************************************************************** |
454 | * |
455 | * Generate a "op icon" instruction. |
456 | */ |
457 | |
458 | void CodeGen::inst_IV(instruction ins, int val) |
459 | { |
460 | getEmitter()->emitIns_I(ins, EA_PTRSIZE, val); |
461 | } |
462 | |
463 | /***************************************************************************** |
464 | * |
465 | * Generate a "op icon" instruction where icon is a handle of type specified |
466 | * by 'flags' |
467 | */ |
468 | |
469 | void CodeGen::inst_IV_handle(instruction ins, int val) |
470 | { |
471 | getEmitter()->emitIns_I(ins, EA_HANDLE_CNS_RELOC, val); |
472 | } |
473 | |
474 | /***************************************************************************** |
475 | * |
476 | * Display a stack frame reference. |
477 | */ |
478 | |
479 | void CodeGen::inst_set_SV_var(GenTree* tree) |
480 | { |
481 | #ifdef DEBUG |
482 | assert(tree && (tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_LCL_VAR_ADDR || tree->gtOper == GT_STORE_LCL_VAR)); |
483 | assert(tree->gtLclVarCommon.gtLclNum < compiler->lvaCount); |
484 | |
485 | getEmitter()->emitVarRefOffs = tree->gtLclVar.gtLclILoffs; |
486 | |
487 | #endif // DEBUG |
488 | } |
489 | |
490 | /***************************************************************************** |
491 | * |
492 | * Generate a "op reg, icon" instruction. |
493 | */ |
494 | |
495 | void CodeGen::inst_RV_IV( |
496 | instruction ins, regNumber reg, target_ssize_t val, emitAttr size, insFlags flags /* = INS_FLAGS_DONT_CARE */) |
497 | { |
498 | #if !defined(_TARGET_64BIT_) |
499 | assert(size != EA_8BYTE); |
500 | #endif |
501 | |
502 | #ifdef _TARGET_ARM_ |
503 | if (arm_Valid_Imm_For_Instr(ins, val, flags)) |
504 | { |
505 | getEmitter()->emitIns_R_I(ins, size, reg, val, flags); |
506 | } |
507 | else if (ins == INS_mov) |
508 | { |
509 | instGen_Set_Reg_To_Imm(size, reg, val); |
510 | } |
511 | else |
512 | { |
513 | // TODO-Cleanup: Add a comment about why this is unreached() for RyuJIT backend. |
514 | unreached(); |
515 | } |
516 | #elif defined(_TARGET_ARM64_) |
517 | // TODO-Arm64-Bug: handle large constants! |
518 | // Probably need something like the ARM case above: if (arm_Valid_Imm_For_Instr(ins, val)) ... |
519 | assert(ins != INS_cmp); |
520 | assert(ins != INS_tst); |
521 | assert(ins != INS_mov); |
522 | getEmitter()->emitIns_R_R_I(ins, size, reg, reg, val); |
523 | #else // !_TARGET_ARM_ |
524 | #ifdef _TARGET_AMD64_ |
525 | // Instead of an 8-byte immediate load, a 4-byte immediate will do fine |
526 | // as the high 4 bytes will be zero anyway. |
527 | if (size == EA_8BYTE && ins == INS_mov && ((val & 0xFFFFFFFF00000000LL) == 0)) |
528 | { |
529 | size = EA_4BYTE; |
530 | getEmitter()->emitIns_R_I(ins, size, reg, val); |
531 | } |
532 | else if (EA_SIZE(size) == EA_8BYTE && ins != INS_mov && (((int)val != val) || EA_IS_CNS_RELOC(size))) |
533 | { |
534 | assert(!"Invalid immediate for inst_RV_IV" ); |
535 | } |
536 | else |
537 | #endif // _TARGET_AMD64_ |
538 | { |
539 | getEmitter()->emitIns_R_I(ins, size, reg, val); |
540 | } |
541 | #endif // !_TARGET_ARM_ |
542 | } |
543 | |
544 | /***************************************************************************** |
545 | * |
546 | * Generate an instruction that has one operand given by a tree (which has |
547 | * been made addressable). |
548 | */ |
549 | |
550 | void CodeGen::inst_TT(instruction ins, GenTree* tree, unsigned offs, int shfv, emitAttr size) |
551 | { |
552 | bool sizeInferred = false; |
553 | |
554 | if (size == EA_UNKNOWN) |
555 | { |
556 | sizeInferred = true; |
557 | if (instIsFP(ins)) |
558 | { |
559 | size = EA_ATTR(genTypeSize(tree->TypeGet())); |
560 | } |
561 | else |
562 | { |
563 | size = emitTypeSize(tree->TypeGet()); |
564 | } |
565 | } |
566 | |
567 | AGAIN: |
568 | |
569 | /* Is this a spilled value? */ |
570 | |
571 | if (tree->gtFlags & GTF_SPILLED) |
572 | { |
573 | assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'" ); |
574 | } |
575 | |
576 | switch (tree->gtOper) |
577 | { |
578 | unsigned varNum; |
579 | |
580 | case GT_LCL_VAR: |
581 | |
582 | inst_set_SV_var(tree); |
583 | goto LCL; |
584 | |
585 | case GT_LCL_FLD: |
586 | |
587 | offs += tree->gtLclFld.gtLclOffs; |
588 | goto LCL; |
589 | |
590 | LCL: |
591 | varNum = tree->gtLclVarCommon.gtLclNum; |
592 | assert(varNum < compiler->lvaCount); |
593 | |
594 | if (shfv) |
595 | { |
596 | getEmitter()->emitIns_S_I(ins, size, varNum, offs, shfv); |
597 | } |
598 | else |
599 | { |
600 | getEmitter()->emitIns_S(ins, size, varNum, offs); |
601 | } |
602 | |
603 | return; |
604 | |
605 | case GT_CLS_VAR: |
606 | // Make sure FP instruction size matches the operand size |
607 | // (We optimized constant doubles to floats when we can, just want to |
608 | // make sure that we don't mistakenly use 8 bytes when the |
609 | // constant. |
610 | assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); |
611 | |
612 | if (shfv) |
613 | { |
614 | getEmitter()->emitIns_C_I(ins, size, tree->gtClsVar.gtClsVarHnd, offs, shfv); |
615 | } |
616 | else |
617 | { |
618 | getEmitter()->emitIns_C(ins, size, tree->gtClsVar.gtClsVarHnd, offs); |
619 | } |
620 | return; |
621 | |
622 | case GT_IND: |
623 | case GT_NULLCHECK: |
624 | case GT_ARR_ELEM: |
625 | { |
626 | assert(!"inst_TT not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM" ); |
627 | } |
628 | break; |
629 | |
630 | #ifdef _TARGET_X86_ |
631 | case GT_CNS_INT: |
632 | // We will get here for GT_MKREFANY from CodeGen::genPushArgList |
633 | assert(offs == 0); |
634 | assert(!shfv); |
635 | if (tree->IsIconHandle()) |
636 | inst_IV_handle(ins, tree->gtIntCon.gtIconVal); |
637 | else |
638 | inst_IV(ins, tree->gtIntCon.gtIconVal); |
639 | break; |
640 | #endif |
641 | |
642 | case GT_COMMA: |
643 | // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() |
644 | tree = tree->gtOp.gtOp2; |
645 | goto AGAIN; |
646 | |
647 | default: |
648 | assert(!"invalid address" ); |
649 | } |
650 | } |
651 | |
652 | /***************************************************************************** |
653 | * |
654 | * Generate an instruction that has one operand given by a tree (which has |
655 | * been made addressable) and another that is a register. |
656 | */ |
657 | |
658 | void CodeGen::inst_TT_RV(instruction ins, GenTree* tree, regNumber reg, unsigned offs, emitAttr size, insFlags flags) |
659 | { |
660 | assert(reg != REG_STK); |
661 | |
662 | AGAIN: |
663 | |
664 | /* Is this a spilled value? */ |
665 | |
666 | if (tree->gtFlags & GTF_SPILLED) |
667 | { |
668 | assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'" ); |
669 | } |
670 | |
671 | if (size == EA_UNKNOWN) |
672 | { |
673 | if (instIsFP(ins)) |
674 | { |
675 | size = EA_ATTR(genTypeSize(tree->TypeGet())); |
676 | } |
677 | else |
678 | { |
679 | size = emitTypeSize(tree->TypeGet()); |
680 | } |
681 | } |
682 | |
683 | switch (tree->gtOper) |
684 | { |
685 | unsigned varNum; |
686 | |
687 | case GT_LCL_VAR: |
688 | |
689 | inst_set_SV_var(tree); |
690 | goto LCL; |
691 | |
692 | case GT_LCL_FLD: |
693 | case GT_STORE_LCL_FLD: |
694 | offs += tree->gtLclFld.gtLclOffs; |
695 | goto LCL; |
696 | |
697 | LCL: |
698 | |
699 | varNum = tree->gtLclVarCommon.gtLclNum; |
700 | assert(varNum < compiler->lvaCount); |
701 | |
702 | #if CPU_LOAD_STORE_ARCH |
703 | if (!getEmitter()->emitInsIsStore(ins)) |
704 | { |
705 | // TODO-LdStArch-Bug: Should regTmp be a dst on the node or an internal reg? |
706 | // Either way, it is not currently being handled by Lowering. |
707 | regNumber regTmp = tree->gtRegNum; |
708 | assert(regTmp != REG_NA); |
709 | getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), size, regTmp, varNum, offs); |
710 | getEmitter()->emitIns_R_R(ins, size, regTmp, reg, flags); |
711 | getEmitter()->emitIns_S_R(ins_Store(tree->TypeGet()), size, regTmp, varNum, offs); |
712 | |
713 | regSet.verifyRegUsed(regTmp); |
714 | } |
715 | else |
716 | #endif |
717 | { |
718 | // ins is a Store instruction |
719 | // |
720 | getEmitter()->emitIns_S_R(ins, size, reg, varNum, offs); |
721 | #ifdef _TARGET_ARM_ |
722 | // If we need to set the flags then add an extra movs reg,reg instruction |
723 | if (flags == INS_FLAGS_SET) |
724 | getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET); |
725 | #endif |
726 | } |
727 | return; |
728 | |
729 | case GT_CLS_VAR: |
730 | // Make sure FP instruction size matches the operand size |
731 | // (We optimized constant doubles to floats when we can, just want to |
732 | // make sure that we don't mistakenly use 8 bytes when the |
733 | // constant). |
734 | assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); |
735 | |
736 | #if CPU_LOAD_STORE_ARCH |
737 | if (!getEmitter()->emitInsIsStore(ins)) |
738 | { |
739 | NYI("Store of GT_CLS_VAR not supported for ARM" ); |
740 | } |
741 | else |
742 | #endif // CPU_LOAD_STORE_ARCH |
743 | { |
744 | getEmitter()->emitIns_C_R(ins, size, tree->gtClsVar.gtClsVarHnd, reg, offs); |
745 | } |
746 | return; |
747 | |
748 | case GT_IND: |
749 | case GT_NULLCHECK: |
750 | case GT_ARR_ELEM: |
751 | { |
752 | assert(!"inst_TT_RV not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM" ); |
753 | } |
754 | break; |
755 | |
756 | case GT_COMMA: |
757 | // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() |
758 | tree = tree->gtOp.gtOp2; |
759 | goto AGAIN; |
760 | |
761 | default: |
762 | assert(!"invalid address" ); |
763 | } |
764 | } |
765 | |
766 | /***************************************************************************** |
767 | * |
768 | * Generate an instruction that has one operand given by a register and the |
769 | * other one by a tree (which has been made addressable). |
770 | */ |
771 | |
772 | void CodeGen::inst_RV_TT(instruction ins, |
773 | regNumber reg, |
774 | GenTree* tree, |
775 | unsigned offs, |
776 | emitAttr size, |
777 | insFlags flags /* = INS_FLAGS_DONT_CARE */) |
778 | { |
779 | assert(reg != REG_STK); |
780 | |
781 | if (size == EA_UNKNOWN) |
782 | { |
783 | if (!instIsFP(ins)) |
784 | { |
785 | size = emitTypeSize(tree->TypeGet()); |
786 | } |
787 | else |
788 | { |
789 | size = EA_ATTR(genTypeSize(tree->TypeGet())); |
790 | } |
791 | } |
792 | |
793 | #ifdef _TARGET_XARCH_ |
794 | #ifdef DEBUG |
795 | // If it is a GC type and the result is not, then either |
796 | // 1) it is an LEA |
797 | // 2) optOptimizeBools() optimized if (ref != 0 && ref != 0) to if (ref & ref) |
798 | // 3) optOptimizeBools() optimized if (ref == 0 || ref == 0) to if (ref | ref) |
799 | // 4) byref - byref = int |
800 | if (tree->gtType == TYP_REF && !EA_IS_GCREF(size)) |
801 | { |
802 | assert((EA_IS_BYREF(size) && ins == INS_add) || (ins == INS_lea || ins == INS_and || ins == INS_or)); |
803 | } |
804 | if (tree->gtType == TYP_BYREF && !EA_IS_BYREF(size)) |
805 | { |
806 | assert(ins == INS_lea || ins == INS_and || ins == INS_or || ins == INS_sub); |
807 | } |
808 | #endif |
809 | #endif |
810 | |
811 | #if CPU_LOAD_STORE_ARCH |
812 | if (ins == INS_mov) |
813 | { |
814 | #if defined(_TARGET_ARM64_) || defined(_TARGET_ARM64_) |
815 | ins = ins_Move_Extend(tree->TypeGet(), false); |
816 | #else |
817 | NYI("CodeGen::inst_RV_TT with INS_mov" ); |
818 | #endif |
819 | } |
820 | #endif // CPU_LOAD_STORE_ARCH |
821 | |
822 | AGAIN: |
823 | |
824 | /* Is this a spilled value? */ |
825 | |
826 | if (tree->gtFlags & GTF_SPILLED) |
827 | { |
828 | assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'" ); |
829 | } |
830 | |
831 | switch (tree->gtOper) |
832 | { |
833 | unsigned varNum; |
834 | |
835 | case GT_LCL_VAR: |
836 | case GT_LCL_VAR_ADDR: |
837 | |
838 | inst_set_SV_var(tree); |
839 | goto LCL; |
840 | |
841 | case GT_LCL_FLD_ADDR: |
842 | case GT_LCL_FLD: |
843 | offs += tree->gtLclFld.gtLclOffs; |
844 | goto LCL; |
845 | |
846 | LCL: |
847 | varNum = tree->gtLclVarCommon.gtLclNum; |
848 | assert(varNum < compiler->lvaCount); |
849 | |
850 | #ifdef _TARGET_ARM_ |
851 | switch (ins) |
852 | { |
853 | case INS_mov: |
854 | ins = ins_Load(tree->TypeGet()); |
855 | __fallthrough; |
856 | |
857 | case INS_lea: |
858 | case INS_ldr: |
859 | case INS_ldrh: |
860 | case INS_ldrb: |
861 | case INS_ldrsh: |
862 | case INS_ldrsb: |
863 | case INS_vldr: |
864 | assert(flags != INS_FLAGS_SET); |
865 | getEmitter()->emitIns_R_S(ins, size, reg, varNum, offs); |
866 | return; |
867 | |
868 | default: |
869 | regNumber regTmp; |
870 | regTmp = tree->gtRegNum; |
871 | |
872 | getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), size, regTmp, varNum, offs); |
873 | getEmitter()->emitIns_R_R(ins, size, reg, regTmp, flags); |
874 | |
875 | regSet.verifyRegUsed(regTmp); |
876 | return; |
877 | } |
878 | #else // !_TARGET_ARM_ |
879 | getEmitter()->emitIns_R_S(ins, size, reg, varNum, offs); |
880 | return; |
881 | #endif // !_TARGET_ARM_ |
882 | |
883 | case GT_CLS_VAR: |
884 | // Make sure FP instruction size matches the operand size |
885 | // (We optimized constant doubles to floats when we can, just want to |
886 | // make sure that we don't mistakenly use 8 bytes when the |
887 | // constant. |
888 | assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); |
889 | |
890 | #if CPU_LOAD_STORE_ARCH |
891 | assert(!"GT_CLS_VAR not supported in ARM backend" ); |
892 | #else // CPU_LOAD_STORE_ARCH |
893 | getEmitter()->emitIns_R_C(ins, size, reg, tree->gtClsVar.gtClsVarHnd, offs); |
894 | #endif // CPU_LOAD_STORE_ARCH |
895 | return; |
896 | |
897 | case GT_IND: |
898 | case GT_NULLCHECK: |
899 | case GT_ARR_ELEM: |
900 | case GT_LEA: |
901 | { |
902 | assert(!"inst_RV_TT not supported for GT_IND, GT_NULLCHECK, GT_ARR_ELEM or GT_LEA" ); |
903 | } |
904 | break; |
905 | |
906 | case GT_CNS_INT: |
907 | |
908 | assert(offs == 0); |
909 | |
910 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntCon::gtIconVal had target_ssize_t type. |
911 | inst_RV_IV(ins, reg, (target_ssize_t)tree->gtIntCon.gtIconVal, emitActualTypeSize(tree->TypeGet()), flags); |
912 | break; |
913 | |
914 | case GT_CNS_LNG: |
915 | |
916 | assert(size == EA_4BYTE || size == EA_8BYTE); |
917 | |
918 | #ifdef _TARGET_AMD64_ |
919 | assert(offs == 0); |
920 | #endif // _TARGET_AMD64_ |
921 | |
922 | target_ssize_t constVal; |
923 | emitAttr size; |
924 | if (offs == 0) |
925 | { |
926 | constVal = (target_ssize_t)(tree->gtLngCon.gtLconVal); |
927 | size = EA_PTRSIZE; |
928 | } |
929 | else |
930 | { |
931 | constVal = (target_ssize_t)(tree->gtLngCon.gtLconVal >> 32); |
932 | size = EA_4BYTE; |
933 | } |
934 | |
935 | inst_RV_IV(ins, reg, constVal, size, flags); |
936 | break; |
937 | |
938 | case GT_COMMA: |
939 | tree = tree->gtOp.gtOp2; |
940 | goto AGAIN; |
941 | |
942 | default: |
943 | assert(!"invalid address" ); |
944 | } |
945 | } |
946 | |
947 | /***************************************************************************** |
948 | * |
949 | * Generate a "shift reg, icon" instruction. |
950 | */ |
951 | |
952 | void CodeGen::inst_RV_SH( |
953 | instruction ins, emitAttr size, regNumber reg, unsigned val, insFlags flags /* = INS_FLAGS_DONT_CARE */) |
954 | { |
955 | #if defined(_TARGET_ARM_) |
956 | |
957 | if (val >= 32) |
958 | val &= 0x1f; |
959 | |
960 | getEmitter()->emitIns_R_I(ins, size, reg, val, flags); |
961 | |
962 | #elif defined(_TARGET_XARCH_) |
963 | |
964 | #ifdef _TARGET_AMD64_ |
965 | // X64 JB BE insures only encodable values make it here. |
966 | // x86 can encode 8 bits, though it masks down to 5 or 6 |
967 | // depending on 32-bit or 64-bit registers are used. |
968 | // Here we will allow anything that is encodable. |
969 | assert(val < 256); |
970 | #endif |
971 | |
972 | ins = genMapShiftInsToShiftByConstantIns(ins, val); |
973 | |
974 | if (val == 1) |
975 | { |
976 | getEmitter()->emitIns_R(ins, size, reg); |
977 | } |
978 | else |
979 | { |
980 | getEmitter()->emitIns_R_I(ins, size, reg, val); |
981 | } |
982 | |
983 | #else |
984 | NYI("inst_RV_SH - unknown target" ); |
985 | #endif // _TARGET_* |
986 | } |
987 | |
988 | /***************************************************************************** |
989 | * |
990 | * Generate a "shift [r/m], icon" instruction. |
991 | */ |
992 | |
993 | void CodeGen::inst_TT_SH(instruction ins, GenTree* tree, unsigned val, unsigned offs) |
994 | { |
995 | #ifdef _TARGET_XARCH_ |
996 | if (val == 0) |
997 | { |
998 | // Shift by 0 - why are you wasting our precious time???? |
999 | return; |
1000 | } |
1001 | |
1002 | ins = genMapShiftInsToShiftByConstantIns(ins, val); |
1003 | if (val == 1) |
1004 | { |
1005 | inst_TT(ins, tree, offs, 0, emitTypeSize(tree->TypeGet())); |
1006 | } |
1007 | else |
1008 | { |
1009 | inst_TT(ins, tree, offs, val, emitTypeSize(tree->TypeGet())); |
1010 | } |
1011 | #endif // _TARGET_XARCH_ |
1012 | |
1013 | #ifdef _TARGET_ARM_ |
1014 | inst_TT(ins, tree, offs, val, emitTypeSize(tree->TypeGet())); |
1015 | #endif |
1016 | } |
1017 | |
1018 | /***************************************************************************** |
1019 | * |
1020 | * Generate a "shift [addr], cl" instruction. |
1021 | */ |
1022 | |
1023 | void CodeGen::inst_TT_CL(instruction ins, GenTree* tree, unsigned offs) |
1024 | { |
1025 | inst_TT(ins, tree, offs, 0, emitTypeSize(tree->TypeGet())); |
1026 | } |
1027 | |
1028 | /***************************************************************************** |
1029 | * |
1030 | * Generate an instruction of the form "op reg1, reg2, icon". |
1031 | */ |
1032 | |
1033 | #if defined(_TARGET_XARCH_) |
1034 | void CodeGen::inst_RV_RV_IV(instruction ins, emitAttr size, regNumber reg1, regNumber reg2, unsigned ival) |
1035 | { |
1036 | assert(ins == INS_shld || ins == INS_shrd || ins == INS_shufps || ins == INS_shufpd || ins == INS_pshufd || |
1037 | ins == INS_cmpps || ins == INS_cmppd || ins == INS_dppd || ins == INS_dpps || ins == INS_insertps || |
1038 | ins == INS_roundps || ins == INS_roundss || ins == INS_roundpd || ins == INS_roundsd); |
1039 | |
1040 | getEmitter()->emitIns_R_R_I(ins, size, reg1, reg2, ival); |
1041 | } |
1042 | #endif |
1043 | |
1044 | /***************************************************************************** |
1045 | * |
1046 | * Generate an instruction with two registers, the second one being a byte |
1047 | * or word register (i.e. this is something like "movzx eax, cl"). |
1048 | */ |
1049 | |
1050 | void CodeGen::inst_RV_RR(instruction ins, emitAttr size, regNumber reg1, regNumber reg2) |
1051 | { |
1052 | assert(size == EA_1BYTE || size == EA_2BYTE); |
1053 | #ifdef _TARGET_XARCH_ |
1054 | assert(ins == INS_movsx || ins == INS_movzx); |
1055 | assert(size != EA_1BYTE || (genRegMask(reg2) & RBM_BYTE_REGS)); |
1056 | #endif |
1057 | |
1058 | getEmitter()->emitIns_R_R(ins, size, reg1, reg2); |
1059 | } |
1060 | |
1061 | /***************************************************************************** |
1062 | * |
1063 | * The following should all end up inline in compiler.hpp at some point. |
1064 | */ |
1065 | |
1066 | void CodeGen::inst_ST_RV(instruction ins, TempDsc* tmp, unsigned ofs, regNumber reg, var_types type) |
1067 | { |
1068 | getEmitter()->emitIns_S_R(ins, emitActualTypeSize(type), reg, tmp->tdTempNum(), ofs); |
1069 | } |
1070 | |
1071 | void CodeGen::inst_ST_IV(instruction ins, TempDsc* tmp, unsigned ofs, int val, var_types type) |
1072 | { |
1073 | getEmitter()->emitIns_S_I(ins, emitActualTypeSize(type), tmp->tdTempNum(), ofs, val); |
1074 | } |
1075 | |
1076 | #if FEATURE_FIXED_OUT_ARGS |
1077 | /***************************************************************************** |
1078 | * |
1079 | * Generate an instruction that references the outgoing argument space |
1080 | * like "str r3, [sp+0x04]" |
1081 | */ |
1082 | |
1083 | void CodeGen::inst_SA_RV(instruction ins, unsigned ofs, regNumber reg, var_types type) |
1084 | { |
1085 | assert(ofs < compiler->lvaOutgoingArgSpaceSize); |
1086 | |
1087 | getEmitter()->emitIns_S_R(ins, emitActualTypeSize(type), reg, compiler->lvaOutgoingArgSpaceVar, ofs); |
1088 | } |
1089 | |
1090 | void CodeGen::inst_SA_IV(instruction ins, unsigned ofs, int val, var_types type) |
1091 | { |
1092 | assert(ofs < compiler->lvaOutgoingArgSpaceSize); |
1093 | |
1094 | getEmitter()->emitIns_S_I(ins, emitActualTypeSize(type), compiler->lvaOutgoingArgSpaceVar, ofs, val); |
1095 | } |
1096 | #endif // FEATURE_FIXED_OUT_ARGS |
1097 | |
1098 | /***************************************************************************** |
1099 | * |
1100 | * Generate an instruction with one register and one operand that is byte |
1101 | * or short (e.g. something like "movzx eax, byte ptr [edx]"). |
1102 | */ |
1103 | |
1104 | void CodeGen::inst_RV_ST(instruction ins, emitAttr size, regNumber reg, GenTree* tree) |
1105 | { |
1106 | assert(size == EA_1BYTE || size == EA_2BYTE); |
1107 | |
1108 | inst_RV_TT(ins, reg, tree, 0, size); |
1109 | } |
1110 | |
1111 | void CodeGen::inst_RV_ST(instruction ins, regNumber reg, TempDsc* tmp, unsigned ofs, var_types type, emitAttr size) |
1112 | { |
1113 | if (size == EA_UNKNOWN) |
1114 | { |
1115 | size = emitActualTypeSize(type); |
1116 | } |
1117 | |
1118 | #ifdef _TARGET_ARM_ |
1119 | switch (ins) |
1120 | { |
1121 | case INS_mov: |
1122 | assert(!"Please call ins_Load(type) to get the load instruction" ); |
1123 | break; |
1124 | |
1125 | case INS_add: |
1126 | case INS_ldr: |
1127 | case INS_ldrh: |
1128 | case INS_ldrb: |
1129 | case INS_ldrsh: |
1130 | case INS_ldrsb: |
1131 | case INS_lea: |
1132 | case INS_vldr: |
1133 | getEmitter()->emitIns_R_S(ins, size, reg, tmp->tdTempNum(), ofs); |
1134 | break; |
1135 | |
1136 | default: |
1137 | assert(!"Default inst_RV_ST case not supported for Arm" ); |
1138 | break; |
1139 | } |
1140 | #else // !_TARGET_ARM_ |
1141 | getEmitter()->emitIns_R_S(ins, size, reg, tmp->tdTempNum(), ofs); |
1142 | #endif // !_TARGET_ARM_ |
1143 | } |
1144 | |
1145 | void CodeGen::inst_mov_RV_ST(regNumber reg, GenTree* tree) |
1146 | { |
1147 | /* Figure out the size of the value being loaded */ |
1148 | |
1149 | emitAttr size = EA_ATTR(genTypeSize(tree->gtType)); |
1150 | instruction loadIns = ins_Move_Extend(tree->TypeGet(), false); |
1151 | |
1152 | if (size < EA_4BYTE) |
1153 | { |
1154 | /* Generate the "movsx/movzx" opcode */ |
1155 | |
1156 | inst_RV_ST(loadIns, size, reg, tree); |
1157 | } |
1158 | else |
1159 | { |
1160 | /* Compute op1 into the target register */ |
1161 | |
1162 | inst_RV_TT(loadIns, reg, tree); |
1163 | } |
1164 | } |
1165 | #ifdef _TARGET_XARCH_ |
1166 | void CodeGen::inst_FS_ST(instruction ins, emitAttr size, TempDsc* tmp, unsigned ofs) |
1167 | { |
1168 | getEmitter()->emitIns_S(ins, size, tmp->tdTempNum(), ofs); |
1169 | } |
1170 | #endif |
1171 | |
1172 | #ifdef _TARGET_ARM_ |
1173 | bool CodeGenInterface::validImmForInstr(instruction ins, target_ssize_t imm, insFlags flags) |
1174 | { |
1175 | if (getEmitter()->emitInsIsLoadOrStore(ins) && !instIsFP(ins)) |
1176 | { |
1177 | return validDispForLdSt(imm, TYP_INT); |
1178 | } |
1179 | |
1180 | bool result = false; |
1181 | switch (ins) |
1182 | { |
1183 | case INS_cmp: |
1184 | case INS_cmn: |
1185 | if (validImmForAlu(imm) || validImmForAlu(-imm)) |
1186 | result = true; |
1187 | break; |
1188 | |
1189 | case INS_and: |
1190 | case INS_bic: |
1191 | case INS_orr: |
1192 | case INS_orn: |
1193 | case INS_mvn: |
1194 | if (validImmForAlu(imm) || validImmForAlu(~imm)) |
1195 | result = true; |
1196 | break; |
1197 | |
1198 | case INS_mov: |
1199 | if (validImmForMov(imm)) |
1200 | result = true; |
1201 | break; |
1202 | |
1203 | case INS_addw: |
1204 | case INS_subw: |
1205 | if ((unsigned_abs(imm) <= 0x00000fff) && (flags != INS_FLAGS_SET)) // 12-bit immediate |
1206 | result = true; |
1207 | break; |
1208 | |
1209 | case INS_add: |
1210 | case INS_sub: |
1211 | if (validImmForAdd(imm, flags)) |
1212 | result = true; |
1213 | break; |
1214 | |
1215 | case INS_tst: |
1216 | case INS_eor: |
1217 | case INS_teq: |
1218 | case INS_adc: |
1219 | case INS_sbc: |
1220 | case INS_rsb: |
1221 | if (validImmForAlu(imm)) |
1222 | result = true; |
1223 | break; |
1224 | |
1225 | case INS_asr: |
1226 | case INS_lsl: |
1227 | case INS_lsr: |
1228 | case INS_ror: |
1229 | if (imm > 0 && imm <= 32) |
1230 | result = true; |
1231 | break; |
1232 | |
1233 | case INS_vstr: |
1234 | case INS_vldr: |
1235 | if ((imm & 0x3FC) == imm) |
1236 | result = true; |
1237 | break; |
1238 | |
1239 | default: |
1240 | break; |
1241 | } |
1242 | return result; |
1243 | } |
1244 | bool CodeGen::arm_Valid_Imm_For_Instr(instruction ins, target_ssize_t imm, insFlags flags) |
1245 | { |
1246 | return validImmForInstr(ins, imm, flags); |
1247 | } |
1248 | |
1249 | bool CodeGenInterface::validDispForLdSt(target_ssize_t disp, var_types type) |
1250 | { |
1251 | if (varTypeIsFloating(type)) |
1252 | { |
1253 | if ((disp & 0x3FC) == disp) |
1254 | return true; |
1255 | else |
1256 | return false; |
1257 | } |
1258 | else |
1259 | { |
1260 | if ((disp >= -0x00ff) && (disp <= 0x0fff)) |
1261 | return true; |
1262 | else |
1263 | return false; |
1264 | } |
1265 | } |
1266 | bool CodeGen::arm_Valid_Disp_For_LdSt(target_ssize_t disp, var_types type) |
1267 | { |
1268 | return validDispForLdSt(disp, type); |
1269 | } |
1270 | |
1271 | bool CodeGenInterface::validImmForAlu(target_ssize_t imm) |
1272 | { |
1273 | return emitter::emitIns_valid_imm_for_alu(imm); |
1274 | } |
1275 | bool CodeGen::arm_Valid_Imm_For_Alu(target_ssize_t imm) |
1276 | { |
1277 | return validImmForAlu(imm); |
1278 | } |
1279 | |
1280 | bool CodeGenInterface::validImmForMov(target_ssize_t imm) |
1281 | { |
1282 | return emitter::emitIns_valid_imm_for_mov(imm); |
1283 | } |
1284 | bool CodeGen::arm_Valid_Imm_For_Mov(target_ssize_t imm) |
1285 | { |
1286 | return validImmForMov(imm); |
1287 | } |
1288 | |
1289 | bool CodeGen::arm_Valid_Imm_For_Small_Mov(regNumber reg, target_ssize_t imm, insFlags flags) |
1290 | { |
1291 | return emitter::emitIns_valid_imm_for_small_mov(reg, imm, flags); |
1292 | } |
1293 | |
1294 | bool CodeGenInterface::validImmForAdd(target_ssize_t imm, insFlags flags) |
1295 | { |
1296 | return emitter::emitIns_valid_imm_for_add(imm, flags); |
1297 | } |
1298 | bool CodeGen::arm_Valid_Imm_For_Add(target_ssize_t imm, insFlags flags) |
1299 | { |
1300 | return emitter::emitIns_valid_imm_for_add(imm, flags); |
1301 | } |
1302 | |
1303 | // Check "add Rd,SP,i10" |
1304 | bool CodeGen::arm_Valid_Imm_For_Add_SP(target_ssize_t imm) |
1305 | { |
1306 | return emitter::emitIns_valid_imm_for_add_sp(imm); |
1307 | } |
1308 | |
1309 | bool CodeGenInterface::validImmForBL(ssize_t addr) |
1310 | { |
1311 | return |
1312 | // If we are running the altjit for NGEN, then assume we can use the "BL" instruction. |
1313 | // This matches the usual behavior for NGEN, since we normally do generate "BL". |
1314 | (!compiler->info.compMatchedVM && compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)) || |
1315 | (compiler->eeGetRelocTypeHint((void*)addr) == IMAGE_REL_BASED_THUMB_BRANCH24); |
1316 | } |
1317 | bool CodeGen::arm_Valid_Imm_For_BL(ssize_t addr) |
1318 | { |
1319 | return validImmForBL(addr); |
1320 | } |
1321 | |
1322 | // Returns true if this instruction writes to a destination register |
1323 | // |
1324 | bool CodeGen::ins_Writes_Dest(instruction ins) |
1325 | { |
1326 | switch (ins) |
1327 | { |
1328 | |
1329 | case INS_cmp: |
1330 | case INS_cmn: |
1331 | case INS_tst: |
1332 | case INS_teq: |
1333 | return false; |
1334 | |
1335 | default: |
1336 | return true; |
1337 | } |
1338 | } |
1339 | #endif // _TARGET_ARM_ |
1340 | |
1341 | #if defined(_TARGET_ARM64_) |
1342 | bool CodeGenInterface::validImmForBL(ssize_t addr) |
1343 | { |
1344 | // On arm64, we always assume a call target is in range and generate a 28-bit relative |
1345 | // 'bl' instruction. If this isn't sufficient range, the VM will generate a jump stub when |
1346 | // we call recordRelocation(). See the IMAGE_REL_ARM64_BRANCH26 case in jitinterface.cpp |
1347 | // (for JIT) or zapinfo.cpp (for NGEN). If we cannot allocate a jump stub, it is fatal. |
1348 | return true; |
1349 | } |
1350 | #endif // _TARGET_ARM64_ |
1351 | |
1352 | /***************************************************************************** |
1353 | * |
1354 | * Get the machine dependent instruction for performing sign/zero extension. |
1355 | * |
1356 | * Parameters |
1357 | * srcType - source type |
1358 | * srcInReg - whether source is in a register |
1359 | */ |
1360 | instruction CodeGen::ins_Move_Extend(var_types srcType, bool srcInReg) |
1361 | { |
1362 | instruction ins = INS_invalid; |
1363 | |
1364 | if (varTypeIsSIMD(srcType)) |
1365 | { |
1366 | #if defined(_TARGET_XARCH_) |
1367 | // SSE2/AVX requires destination to be a reg always. |
1368 | // If src is in reg means, it is a reg-reg move. |
1369 | // |
1370 | // SSE2 Note: always prefer movaps/movups over movapd/movupd since the |
1371 | // former doesn't require 66h prefix and one byte smaller than the |
1372 | // latter. |
1373 | // |
1374 | // TODO-CQ: based on whether src type is aligned use movaps instead |
1375 | |
1376 | return (srcInReg) ? INS_movaps : INS_movups; |
1377 | #elif defined(_TARGET_ARM64_) |
1378 | return (srcInReg) ? INS_mov : ins_Load(srcType); |
1379 | #else // !defined(_TARGET_ARM64_) && !defined(_TARGET_XARCH_) |
1380 | assert(!"unhandled SIMD type" ); |
1381 | #endif // !defined(_TARGET_ARM64_) && !defined(_TARGET_XARCH_) |
1382 | } |
1383 | |
1384 | #if defined(_TARGET_XARCH_) |
1385 | if (varTypeIsFloating(srcType)) |
1386 | { |
1387 | if (srcType == TYP_DOUBLE) |
1388 | { |
1389 | return (srcInReg) ? INS_movaps : INS_movsdsse2; |
1390 | } |
1391 | else if (srcType == TYP_FLOAT) |
1392 | { |
1393 | return (srcInReg) ? INS_movaps : INS_movss; |
1394 | } |
1395 | else |
1396 | { |
1397 | assert(!"unhandled floating type" ); |
1398 | } |
1399 | } |
1400 | #elif defined(_TARGET_ARM_) |
1401 | if (varTypeIsFloating(srcType)) |
1402 | return INS_vmov; |
1403 | #else |
1404 | assert(!varTypeIsFloating(srcType)); |
1405 | #endif |
1406 | |
1407 | #if defined(_TARGET_XARCH_) |
1408 | if (!varTypeIsSmall(srcType)) |
1409 | { |
1410 | ins = INS_mov; |
1411 | } |
1412 | else if (varTypeIsUnsigned(srcType)) |
1413 | { |
1414 | ins = INS_movzx; |
1415 | } |
1416 | else |
1417 | { |
1418 | ins = INS_movsx; |
1419 | } |
1420 | #elif defined(_TARGET_ARM_) |
1421 | // |
1422 | // Register to Register zero/sign extend operation |
1423 | // |
1424 | if (srcInReg) |
1425 | { |
1426 | if (!varTypeIsSmall(srcType)) |
1427 | { |
1428 | ins = INS_mov; |
1429 | } |
1430 | else if (varTypeIsUnsigned(srcType)) |
1431 | { |
1432 | if (varTypeIsByte(srcType)) |
1433 | ins = INS_uxtb; |
1434 | else |
1435 | ins = INS_uxth; |
1436 | } |
1437 | else |
1438 | { |
1439 | if (varTypeIsByte(srcType)) |
1440 | ins = INS_sxtb; |
1441 | else |
1442 | ins = INS_sxth; |
1443 | } |
1444 | } |
1445 | else |
1446 | { |
1447 | ins = ins_Load(srcType); |
1448 | } |
1449 | #elif defined(_TARGET_ARM64_) |
1450 | // |
1451 | // Register to Register zero/sign extend operation |
1452 | // |
1453 | if (srcInReg) |
1454 | { |
1455 | if (varTypeIsUnsigned(srcType)) |
1456 | { |
1457 | if (varTypeIsByte(srcType)) |
1458 | { |
1459 | ins = INS_uxtb; |
1460 | } |
1461 | else if (varTypeIsShort(srcType)) |
1462 | { |
1463 | ins = INS_uxth; |
1464 | } |
1465 | else |
1466 | { |
1467 | // A mov Rd, Rm instruction performs the zero extend |
1468 | // for the upper 32 bits when the size is EA_4BYTE |
1469 | |
1470 | ins = INS_mov; |
1471 | } |
1472 | } |
1473 | else |
1474 | { |
1475 | if (varTypeIsByte(srcType)) |
1476 | { |
1477 | ins = INS_sxtb; |
1478 | } |
1479 | else if (varTypeIsShort(srcType)) |
1480 | { |
1481 | ins = INS_sxth; |
1482 | } |
1483 | else |
1484 | { |
1485 | if (srcType == TYP_INT) |
1486 | { |
1487 | ins = INS_sxtw; |
1488 | } |
1489 | else |
1490 | { |
1491 | ins = INS_mov; |
1492 | } |
1493 | } |
1494 | } |
1495 | } |
1496 | else |
1497 | { |
1498 | ins = ins_Load(srcType); |
1499 | } |
1500 | #else |
1501 | NYI("ins_Move_Extend" ); |
1502 | #endif |
1503 | assert(ins != INS_invalid); |
1504 | return ins; |
1505 | } |
1506 | |
1507 | /***************************************************************************** |
1508 | * |
1509 | * Get the machine dependent instruction for performing a load for srcType |
1510 | * |
1511 | * Parameters |
1512 | * srcType - source type |
1513 | * aligned - whether source is properly aligned if srcType is a SIMD type |
1514 | */ |
1515 | instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false*/) |
1516 | { |
1517 | instruction ins = INS_invalid; |
1518 | |
1519 | if (varTypeIsSIMD(srcType)) |
1520 | { |
1521 | #if defined(_TARGET_XARCH_) |
1522 | #ifdef FEATURE_SIMD |
1523 | if (srcType == TYP_SIMD8) |
1524 | { |
1525 | return INS_movsdsse2; |
1526 | } |
1527 | else |
1528 | #endif // FEATURE_SIMD |
1529 | if (compiler->canUseVexEncoding()) |
1530 | { |
1531 | return (aligned) ? INS_movapd : INS_movupd; |
1532 | } |
1533 | else |
1534 | { |
1535 | // SSE2 Note: always prefer movaps/movups over movapd/movupd since the |
1536 | // former doesn't require 66h prefix and one byte smaller than the |
1537 | // latter. |
1538 | return (aligned) ? INS_movaps : INS_movups; |
1539 | } |
1540 | #elif defined(_TARGET_ARM64_) |
1541 | return INS_ldr; |
1542 | #else |
1543 | assert(!"ins_Load with SIMD type" ); |
1544 | #endif |
1545 | } |
1546 | |
1547 | if (varTypeIsFloating(srcType)) |
1548 | { |
1549 | #if defined(_TARGET_XARCH_) |
1550 | if (srcType == TYP_DOUBLE) |
1551 | { |
1552 | return INS_movsdsse2; |
1553 | } |
1554 | else if (srcType == TYP_FLOAT) |
1555 | { |
1556 | return INS_movss; |
1557 | } |
1558 | else |
1559 | { |
1560 | assert(!"unhandled floating type" ); |
1561 | } |
1562 | #elif defined(_TARGET_ARM64_) |
1563 | return INS_ldr; |
1564 | #elif defined(_TARGET_ARM_) |
1565 | return INS_vldr; |
1566 | #else |
1567 | assert(!varTypeIsFloating(srcType)); |
1568 | #endif |
1569 | } |
1570 | |
1571 | #if defined(_TARGET_XARCH_) |
1572 | if (!varTypeIsSmall(srcType)) |
1573 | { |
1574 | ins = INS_mov; |
1575 | } |
1576 | else if (varTypeIsUnsigned(srcType)) |
1577 | { |
1578 | ins = INS_movzx; |
1579 | } |
1580 | else |
1581 | { |
1582 | ins = INS_movsx; |
1583 | } |
1584 | |
1585 | #elif defined(_TARGET_ARMARCH_) |
1586 | if (!varTypeIsSmall(srcType)) |
1587 | { |
1588 | #if defined(_TARGET_ARM64_) |
1589 | if (!varTypeIsI(srcType) && !varTypeIsUnsigned(srcType)) |
1590 | { |
1591 | ins = INS_ldrsw; |
1592 | } |
1593 | else |
1594 | #endif // defined(_TARGET_ARM64_) |
1595 | { |
1596 | ins = INS_ldr; |
1597 | } |
1598 | } |
1599 | else if (varTypeIsByte(srcType)) |
1600 | { |
1601 | if (varTypeIsUnsigned(srcType)) |
1602 | ins = INS_ldrb; |
1603 | else |
1604 | ins = INS_ldrsb; |
1605 | } |
1606 | else if (varTypeIsShort(srcType)) |
1607 | { |
1608 | if (varTypeIsUnsigned(srcType)) |
1609 | ins = INS_ldrh; |
1610 | else |
1611 | ins = INS_ldrsh; |
1612 | } |
1613 | #else |
1614 | NYI("ins_Load" ); |
1615 | #endif |
1616 | |
1617 | assert(ins != INS_invalid); |
1618 | return ins; |
1619 | } |
1620 | |
1621 | /***************************************************************************** |
1622 | * |
1623 | * Get the machine dependent instruction for performing a reg-reg copy for dstType |
1624 | * |
1625 | * Parameters |
1626 | * dstType - destination type |
1627 | */ |
1628 | instruction CodeGen::ins_Copy(var_types dstType) |
1629 | { |
1630 | #if defined(_TARGET_XARCH_) |
1631 | if (varTypeIsSIMD(dstType)) |
1632 | { |
1633 | return INS_movaps; |
1634 | } |
1635 | else if (varTypeIsFloating(dstType)) |
1636 | { |
1637 | // Both float and double copy can use movaps |
1638 | return INS_movaps; |
1639 | } |
1640 | else |
1641 | { |
1642 | return INS_mov; |
1643 | } |
1644 | #elif defined(_TARGET_ARM64_) |
1645 | if (varTypeIsFloating(dstType)) |
1646 | { |
1647 | return INS_fmov; |
1648 | } |
1649 | else |
1650 | { |
1651 | return INS_mov; |
1652 | } |
1653 | #elif defined(_TARGET_ARM_) |
1654 | assert(!varTypeIsSIMD(dstType)); |
1655 | if (varTypeIsFloating(dstType)) |
1656 | { |
1657 | return INS_vmov; |
1658 | } |
1659 | else |
1660 | { |
1661 | return INS_mov; |
1662 | } |
1663 | #elif defined(_TARGET_X86_) |
1664 | assert(!varTypeIsSIMD(dstType)); |
1665 | assert(!varTypeIsFloating(dstType)); |
1666 | return INS_mov; |
1667 | #else // _TARGET_* |
1668 | #error "Unknown _TARGET_" |
1669 | #endif |
1670 | } |
1671 | |
1672 | /***************************************************************************** |
1673 | * |
1674 | * Get the machine dependent instruction for performing a store for dstType |
1675 | * |
1676 | * Parameters |
1677 | * dstType - destination type |
1678 | * aligned - whether destination is properly aligned if dstType is a SIMD type |
1679 | */ |
1680 | instruction CodeGenInterface::ins_Store(var_types dstType, bool aligned /*=false*/) |
1681 | { |
1682 | instruction ins = INS_invalid; |
1683 | |
1684 | #if defined(_TARGET_XARCH_) |
1685 | if (varTypeIsSIMD(dstType)) |
1686 | { |
1687 | #ifdef FEATURE_SIMD |
1688 | if (dstType == TYP_SIMD8) |
1689 | { |
1690 | return INS_movsdsse2; |
1691 | } |
1692 | else |
1693 | #endif // FEATURE_SIMD |
1694 | if (compiler->canUseVexEncoding()) |
1695 | { |
1696 | return (aligned) ? INS_movapd : INS_movupd; |
1697 | } |
1698 | else |
1699 | { |
1700 | // SSE2 Note: always prefer movaps/movups over movapd/movupd since the |
1701 | // former doesn't require 66h prefix and one byte smaller than the |
1702 | // latter. |
1703 | return (aligned) ? INS_movaps : INS_movups; |
1704 | } |
1705 | } |
1706 | else if (varTypeIsFloating(dstType)) |
1707 | { |
1708 | if (dstType == TYP_DOUBLE) |
1709 | { |
1710 | return INS_movsdsse2; |
1711 | } |
1712 | else if (dstType == TYP_FLOAT) |
1713 | { |
1714 | return INS_movss; |
1715 | } |
1716 | else |
1717 | { |
1718 | assert(!"unhandled floating type" ); |
1719 | } |
1720 | } |
1721 | #elif defined(_TARGET_ARM64_) |
1722 | if (varTypeIsSIMD(dstType) || varTypeIsFloating(dstType)) |
1723 | { |
1724 | // All sizes of SIMD and FP instructions use INS_str |
1725 | return INS_str; |
1726 | } |
1727 | #elif defined(_TARGET_ARM_) |
1728 | assert(!varTypeIsSIMD(dstType)); |
1729 | if (varTypeIsFloating(dstType)) |
1730 | { |
1731 | return INS_vstr; |
1732 | } |
1733 | #else |
1734 | assert(!varTypeIsSIMD(dstType)); |
1735 | assert(!varTypeIsFloating(dstType)); |
1736 | #endif |
1737 | |
1738 | #if defined(_TARGET_XARCH_) |
1739 | ins = INS_mov; |
1740 | #elif defined(_TARGET_ARMARCH_) |
1741 | if (!varTypeIsSmall(dstType)) |
1742 | ins = INS_str; |
1743 | else if (varTypeIsByte(dstType)) |
1744 | ins = INS_strb; |
1745 | else if (varTypeIsShort(dstType)) |
1746 | ins = INS_strh; |
1747 | #else |
1748 | NYI("ins_Store" ); |
1749 | #endif |
1750 | |
1751 | assert(ins != INS_invalid); |
1752 | return ins; |
1753 | } |
1754 | |
1755 | #if defined(_TARGET_XARCH_) |
1756 | |
1757 | bool CodeGen::isMoveIns(instruction ins) |
1758 | { |
1759 | return (ins == INS_mov); |
1760 | } |
1761 | |
1762 | instruction CodeGenInterface::ins_FloatLoad(var_types type) |
1763 | { |
1764 | // Do Not use this routine in RyuJIT backend. Instead use ins_Load()/ins_Store() |
1765 | unreached(); |
1766 | } |
1767 | |
1768 | // everything is just an addressing mode variation on x64 |
1769 | instruction CodeGen::ins_FloatStore(var_types type) |
1770 | { |
1771 | // Do Not use this routine in RyuJIT backend. Instead use ins_Store() |
1772 | unreached(); |
1773 | } |
1774 | |
1775 | instruction CodeGen::ins_FloatCopy(var_types type) |
1776 | { |
1777 | // Do Not use this routine in RyuJIT backend. Instead use ins_Load(). |
1778 | unreached(); |
1779 | } |
1780 | |
1781 | instruction CodeGen::ins_FloatCompare(var_types type) |
1782 | { |
1783 | return (type == TYP_FLOAT) ? INS_ucomiss : INS_ucomisd; |
1784 | } |
1785 | |
1786 | instruction CodeGen::ins_CopyIntToFloat(var_types srcType, var_types dstType) |
1787 | { |
1788 | // On SSE2/AVX - the same instruction is used for moving double/quad word to XMM/YMM register. |
1789 | assert((srcType == TYP_INT) || (srcType == TYP_UINT) || (srcType == TYP_LONG) || (srcType == TYP_ULONG)); |
1790 | |
1791 | #if !defined(_TARGET_64BIT_) |
1792 | // No 64-bit registers on x86. |
1793 | assert((srcType != TYP_LONG) && (srcType != TYP_ULONG)); |
1794 | #endif // !defined(_TARGET_64BIT_) |
1795 | |
1796 | return INS_mov_i2xmm; |
1797 | } |
1798 | |
1799 | instruction CodeGen::ins_CopyFloatToInt(var_types srcType, var_types dstType) |
1800 | { |
1801 | // On SSE2/AVX - the same instruction is used for moving double/quad word of XMM/YMM to an integer register. |
1802 | assert((dstType == TYP_INT) || (dstType == TYP_UINT) || (dstType == TYP_LONG) || (dstType == TYP_ULONG)); |
1803 | |
1804 | #if !defined(_TARGET_64BIT_) |
1805 | // No 64-bit registers on x86. |
1806 | assert((dstType != TYP_LONG) && (dstType != TYP_ULONG)); |
1807 | #endif // !defined(_TARGET_64BIT_) |
1808 | |
1809 | return INS_mov_xmm2i; |
1810 | } |
1811 | |
1812 | instruction CodeGen::ins_MathOp(genTreeOps oper, var_types type) |
1813 | { |
1814 | switch (oper) |
1815 | { |
1816 | case GT_ADD: |
1817 | return type == TYP_DOUBLE ? INS_addsd : INS_addss; |
1818 | case GT_SUB: |
1819 | return type == TYP_DOUBLE ? INS_subsd : INS_subss; |
1820 | case GT_MUL: |
1821 | return type == TYP_DOUBLE ? INS_mulsd : INS_mulss; |
1822 | case GT_DIV: |
1823 | return type == TYP_DOUBLE ? INS_divsd : INS_divss; |
1824 | default: |
1825 | unreached(); |
1826 | } |
1827 | } |
1828 | |
1829 | instruction CodeGen::ins_FloatSqrt(var_types type) |
1830 | { |
1831 | instruction ins = INS_invalid; |
1832 | |
1833 | if (type == TYP_DOUBLE) |
1834 | { |
1835 | ins = INS_sqrtsd; |
1836 | } |
1837 | else if (type == TYP_FLOAT) |
1838 | { |
1839 | ins = INS_sqrtss; |
1840 | } |
1841 | else |
1842 | { |
1843 | assert(!"ins_FloatSqrt: Unsupported type" ); |
1844 | unreached(); |
1845 | } |
1846 | |
1847 | return ins; |
1848 | } |
1849 | |
1850 | // Conversions to or from floating point values |
1851 | instruction CodeGen::ins_FloatConv(var_types to, var_types from) |
1852 | { |
1853 | // AVX: For now we support only conversion from Int/Long -> float |
1854 | |
1855 | switch (from) |
1856 | { |
1857 | // int/long -> float/double use the same instruction but type size would be different. |
1858 | case TYP_INT: |
1859 | case TYP_LONG: |
1860 | switch (to) |
1861 | { |
1862 | case TYP_FLOAT: |
1863 | return INS_cvtsi2ss; |
1864 | case TYP_DOUBLE: |
1865 | return INS_cvtsi2sd; |
1866 | default: |
1867 | unreached(); |
1868 | } |
1869 | break; |
1870 | |
1871 | case TYP_FLOAT: |
1872 | switch (to) |
1873 | { |
1874 | case TYP_INT: |
1875 | return INS_cvttss2si; |
1876 | case TYP_LONG: |
1877 | return INS_cvttss2si; |
1878 | case TYP_FLOAT: |
1879 | return ins_Move_Extend(TYP_FLOAT, false); |
1880 | case TYP_DOUBLE: |
1881 | return INS_cvtss2sd; |
1882 | default: |
1883 | unreached(); |
1884 | } |
1885 | break; |
1886 | |
1887 | case TYP_DOUBLE: |
1888 | switch (to) |
1889 | { |
1890 | case TYP_INT: |
1891 | return INS_cvttsd2si; |
1892 | case TYP_LONG: |
1893 | return INS_cvttsd2si; |
1894 | case TYP_FLOAT: |
1895 | return INS_cvtsd2ss; |
1896 | case TYP_DOUBLE: |
1897 | return ins_Move_Extend(TYP_DOUBLE, false); |
1898 | default: |
1899 | unreached(); |
1900 | } |
1901 | break; |
1902 | |
1903 | default: |
1904 | unreached(); |
1905 | } |
1906 | } |
1907 | |
1908 | #elif defined(_TARGET_ARM_) |
1909 | |
1910 | bool CodeGen::isMoveIns(instruction ins) |
1911 | { |
1912 | return (ins == INS_vmov) || (ins == INS_mov); |
1913 | } |
1914 | |
1915 | instruction CodeGenInterface::ins_FloatLoad(var_types type) |
1916 | { |
1917 | assert(type == TYP_DOUBLE || type == TYP_FLOAT); |
1918 | return INS_vldr; |
1919 | } |
1920 | instruction CodeGen::ins_FloatStore(var_types type) |
1921 | { |
1922 | assert(type == TYP_DOUBLE || type == TYP_FLOAT); |
1923 | return INS_vstr; |
1924 | } |
1925 | instruction CodeGen::ins_FloatCopy(var_types type) |
1926 | { |
1927 | assert(type == TYP_DOUBLE || type == TYP_FLOAT); |
1928 | return INS_vmov; |
1929 | } |
1930 | |
1931 | instruction CodeGen::ins_CopyIntToFloat(var_types srcType, var_types dstType) |
1932 | { |
1933 | assert((dstType == TYP_FLOAT) || (dstType == TYP_DOUBLE)); |
1934 | assert((srcType == TYP_INT) || (srcType == TYP_UINT) || (srcType == TYP_LONG) || (srcType == TYP_ULONG)); |
1935 | |
1936 | if ((srcType == TYP_LONG) || (srcType == TYP_ULONG)) |
1937 | { |
1938 | return INS_vmov_i2d; |
1939 | } |
1940 | else |
1941 | { |
1942 | return INS_vmov_i2f; |
1943 | } |
1944 | } |
1945 | |
1946 | instruction CodeGen::ins_CopyFloatToInt(var_types srcType, var_types dstType) |
1947 | { |
1948 | assert((srcType == TYP_FLOAT) || (srcType == TYP_DOUBLE)); |
1949 | assert((dstType == TYP_INT) || (dstType == TYP_UINT) || (dstType == TYP_LONG) || (dstType == TYP_ULONG)); |
1950 | |
1951 | if ((dstType == TYP_LONG) || (dstType == TYP_ULONG)) |
1952 | { |
1953 | return INS_vmov_d2i; |
1954 | } |
1955 | else |
1956 | { |
1957 | return INS_vmov_f2i; |
1958 | } |
1959 | } |
1960 | |
1961 | instruction CodeGen::ins_FloatCompare(var_types type) |
1962 | { |
1963 | // Not used and not implemented |
1964 | unreached(); |
1965 | } |
1966 | |
1967 | instruction CodeGen::ins_FloatSqrt(var_types type) |
1968 | { |
1969 | // Not used and not implemented |
1970 | unreached(); |
1971 | } |
1972 | |
1973 | instruction CodeGen::ins_MathOp(genTreeOps oper, var_types type) |
1974 | { |
1975 | switch (oper) |
1976 | { |
1977 | case GT_ADD: |
1978 | return INS_vadd; |
1979 | case GT_SUB: |
1980 | return INS_vsub; |
1981 | case GT_MUL: |
1982 | return INS_vmul; |
1983 | case GT_DIV: |
1984 | return INS_vdiv; |
1985 | case GT_NEG: |
1986 | return INS_vneg; |
1987 | default: |
1988 | unreached(); |
1989 | } |
1990 | } |
1991 | |
1992 | instruction CodeGen::ins_FloatConv(var_types to, var_types from) |
1993 | { |
1994 | switch (from) |
1995 | { |
1996 | case TYP_INT: |
1997 | switch (to) |
1998 | { |
1999 | case TYP_FLOAT: |
2000 | return INS_vcvt_i2f; |
2001 | case TYP_DOUBLE: |
2002 | return INS_vcvt_i2d; |
2003 | default: |
2004 | unreached(); |
2005 | } |
2006 | break; |
2007 | case TYP_UINT: |
2008 | switch (to) |
2009 | { |
2010 | case TYP_FLOAT: |
2011 | return INS_vcvt_u2f; |
2012 | case TYP_DOUBLE: |
2013 | return INS_vcvt_u2d; |
2014 | default: |
2015 | unreached(); |
2016 | } |
2017 | break; |
2018 | case TYP_LONG: |
2019 | switch (to) |
2020 | { |
2021 | case TYP_FLOAT: |
2022 | NYI("long to float" ); |
2023 | case TYP_DOUBLE: |
2024 | NYI("long to double" ); |
2025 | default: |
2026 | unreached(); |
2027 | } |
2028 | break; |
2029 | case TYP_FLOAT: |
2030 | switch (to) |
2031 | { |
2032 | case TYP_INT: |
2033 | return INS_vcvt_f2i; |
2034 | case TYP_UINT: |
2035 | return INS_vcvt_f2u; |
2036 | case TYP_LONG: |
2037 | NYI("float to long" ); |
2038 | case TYP_DOUBLE: |
2039 | return INS_vcvt_f2d; |
2040 | case TYP_FLOAT: |
2041 | return INS_vmov; |
2042 | default: |
2043 | unreached(); |
2044 | } |
2045 | break; |
2046 | case TYP_DOUBLE: |
2047 | switch (to) |
2048 | { |
2049 | case TYP_INT: |
2050 | return INS_vcvt_d2i; |
2051 | case TYP_UINT: |
2052 | return INS_vcvt_d2u; |
2053 | case TYP_LONG: |
2054 | NYI("double to long" ); |
2055 | case TYP_FLOAT: |
2056 | return INS_vcvt_d2f; |
2057 | case TYP_DOUBLE: |
2058 | return INS_vmov; |
2059 | default: |
2060 | unreached(); |
2061 | } |
2062 | break; |
2063 | default: |
2064 | unreached(); |
2065 | } |
2066 | } |
2067 | |
2068 | #endif // #elif defined(_TARGET_ARM_) |
2069 | |
2070 | /***************************************************************************** |
2071 | * |
2072 | * Machine independent way to return |
2073 | */ |
2074 | void CodeGen::instGen_Return(unsigned stkArgSize) |
2075 | { |
2076 | #if defined(_TARGET_XARCH_) |
2077 | if (stkArgSize == 0) |
2078 | { |
2079 | instGen(INS_ret); |
2080 | } |
2081 | else |
2082 | { |
2083 | inst_IV(INS_ret, stkArgSize); |
2084 | } |
2085 | #elif defined(_TARGET_ARM_) |
2086 | // |
2087 | // The return on ARM is folded into the pop multiple instruction |
2088 | // and as we do not know the exact set of registers that we will |
2089 | // need to restore (pop) when we first call instGen_Return we will |
2090 | // instead just not emit anything for this method on the ARM |
2091 | // The return will be part of the pop multiple and that will be |
2092 | // part of the epilog that is generated by genFnEpilog() |
2093 | #elif defined(_TARGET_ARM64_) |
2094 | // This function shouldn't be used on ARM64. |
2095 | unreached(); |
2096 | #else |
2097 | NYI("instGen_Return" ); |
2098 | #endif |
2099 | } |
2100 | |
2101 | /***************************************************************************** |
2102 | * |
2103 | * Emit a MemoryBarrier instruction |
2104 | * |
2105 | * Note: all MemoryBarriers instructions can be removed by |
2106 | * SET COMPlus_JitNoMemoryBarriers=1 |
2107 | */ |
2108 | #ifdef _TARGET_ARM64_ |
2109 | void CodeGen::instGen_MemoryBarrier(insBarrier barrierType) |
2110 | #else |
2111 | void CodeGen::instGen_MemoryBarrier() |
2112 | #endif |
2113 | { |
2114 | #ifdef DEBUG |
2115 | if (JitConfig.JitNoMemoryBarriers() == 1) |
2116 | { |
2117 | return; |
2118 | } |
2119 | #endif // DEBUG |
2120 | |
2121 | #if defined(_TARGET_XARCH_) |
2122 | instGen(INS_lock); |
2123 | getEmitter()->emitIns_I_AR(INS_or, EA_4BYTE, 0, REG_SPBASE, 0); |
2124 | #elif defined(_TARGET_ARM_) |
2125 | getEmitter()->emitIns_I(INS_dmb, EA_4BYTE, 0xf); |
2126 | #elif defined(_TARGET_ARM64_) |
2127 | getEmitter()->emitIns_BARR(INS_dmb, barrierType); |
2128 | #else |
2129 | #error "Unknown _TARGET_" |
2130 | #endif |
2131 | } |
2132 | |
2133 | /***************************************************************************** |
2134 | * |
2135 | * Machine independent way to move a Zero value into a register |
2136 | */ |
2137 | void CodeGen::instGen_Set_Reg_To_Zero(emitAttr size, regNumber reg, insFlags flags) |
2138 | { |
2139 | #if defined(_TARGET_XARCH_) |
2140 | getEmitter()->emitIns_R_R(INS_xor, size, reg, reg); |
2141 | #elif defined(_TARGET_ARMARCH_) |
2142 | getEmitter()->emitIns_R_I(INS_mov, size, reg, 0 ARM_ARG(flags)); |
2143 | #else |
2144 | #error "Unknown _TARGET_" |
2145 | #endif |
2146 | regSet.verifyRegUsed(reg); |
2147 | } |
2148 | |
2149 | /***************************************************************************** |
2150 | * |
2151 | * Machine independent way to set the flags based on |
2152 | * comparing a register with zero |
2153 | */ |
2154 | void CodeGen::instGen_Compare_Reg_To_Zero(emitAttr size, regNumber reg) |
2155 | { |
2156 | #if defined(_TARGET_XARCH_) |
2157 | getEmitter()->emitIns_R_R(INS_test, size, reg, reg); |
2158 | #elif defined(_TARGET_ARMARCH_) |
2159 | getEmitter()->emitIns_R_I(INS_cmp, size, reg, 0); |
2160 | #else |
2161 | #error "Unknown _TARGET_" |
2162 | #endif |
2163 | } |
2164 | |
2165 | /***************************************************************************** |
2166 | * |
2167 | * Machine independent way to set the flags based upon |
2168 | * comparing a register with another register |
2169 | */ |
2170 | void CodeGen::instGen_Compare_Reg_To_Reg(emitAttr size, regNumber reg1, regNumber reg2) |
2171 | { |
2172 | #if defined(_TARGET_XARCH_) || defined(_TARGET_ARMARCH_) |
2173 | getEmitter()->emitIns_R_R(INS_cmp, size, reg1, reg2); |
2174 | #else |
2175 | #error "Unknown _TARGET_" |
2176 | #endif |
2177 | } |
2178 | |
2179 | /***************************************************************************** |
2180 | * |
2181 | * Machine independent way to set the flags based upon |
2182 | * comparing a register with an immediate |
2183 | */ |
2184 | void CodeGen::instGen_Compare_Reg_To_Imm(emitAttr size, regNumber reg, target_ssize_t imm) |
2185 | { |
2186 | if (imm == 0) |
2187 | { |
2188 | instGen_Compare_Reg_To_Zero(size, reg); |
2189 | } |
2190 | else |
2191 | { |
2192 | #if defined(_TARGET_XARCH_) |
2193 | #if defined(_TARGET_AMD64_) |
2194 | if ((EA_SIZE(size) == EA_8BYTE) && (((int)imm != (ssize_t)imm) || EA_IS_CNS_RELOC(size))) |
2195 | { |
2196 | assert(!"Invalid immediate for instGen_Compare_Reg_To_Imm" ); |
2197 | } |
2198 | else |
2199 | #endif // _TARGET_AMD64_ |
2200 | { |
2201 | getEmitter()->emitIns_R_I(INS_cmp, size, reg, imm); |
2202 | } |
2203 | #elif defined(_TARGET_ARM_) |
2204 | if (arm_Valid_Imm_For_Alu(imm) || arm_Valid_Imm_For_Alu(-imm)) |
2205 | { |
2206 | getEmitter()->emitIns_R_I(INS_cmp, size, reg, imm); |
2207 | } |
2208 | else // We need a scratch register |
2209 | { |
2210 | assert(!"Invalid immediate for instGen_Compare_Reg_To_Imm" ); |
2211 | } |
2212 | #elif defined(_TARGET_ARM64_) |
2213 | if (true) // TODO-ARM64-NYI: arm_Valid_Imm_For_Alu(imm) || arm_Valid_Imm_For_Alu(-imm)) |
2214 | { |
2215 | getEmitter()->emitIns_R_I(INS_cmp, size, reg, imm); |
2216 | } |
2217 | else // We need a scratch register |
2218 | { |
2219 | assert(!"Invalid immediate for instGen_Compare_Reg_To_Imm" ); |
2220 | } |
2221 | #else |
2222 | #error "Unknown _TARGET_" |
2223 | #endif |
2224 | } |
2225 | } |
2226 | |
2227 | /***************************************************************************** |
2228 | * |
2229 | * Machine independent way to move a stack based local variable into a register |
2230 | */ |
2231 | void CodeGen::instGen_Load_Reg_From_Lcl(var_types srcType, regNumber dstReg, int varNum, int offs) |
2232 | { |
2233 | emitAttr size = emitTypeSize(srcType); |
2234 | |
2235 | getEmitter()->emitIns_R_S(ins_Load(srcType), size, dstReg, varNum, offs); |
2236 | } |
2237 | |
2238 | /***************************************************************************** |
2239 | * |
2240 | * Machine independent way to move a register into a stack based local variable |
2241 | */ |
2242 | void CodeGen::instGen_Store_Reg_Into_Lcl(var_types dstType, regNumber srcReg, int varNum, int offs) |
2243 | { |
2244 | emitAttr size = emitTypeSize(dstType); |
2245 | |
2246 | getEmitter()->emitIns_S_R(ins_Store(dstType), size, srcReg, varNum, offs); |
2247 | } |
2248 | |
2249 | /***************************************************************************** |
2250 | * |
2251 | * Machine independent way to move an immediate into a stack based local variable |
2252 | */ |
2253 | void CodeGen::instGen_Store_Imm_Into_Lcl( |
2254 | var_types dstType, emitAttr sizeAttr, ssize_t imm, int varNum, int offs, regNumber regToUse) |
2255 | { |
2256 | #ifdef _TARGET_XARCH_ |
2257 | #ifdef _TARGET_AMD64_ |
2258 | if ((EA_SIZE(sizeAttr) == EA_8BYTE) && (((int)imm != (ssize_t)imm) || EA_IS_CNS_RELOC(sizeAttr))) |
2259 | { |
2260 | assert(!"Invalid immediate for instGen_Store_Imm_Into_Lcl" ); |
2261 | } |
2262 | else |
2263 | #endif // _TARGET_AMD64_ |
2264 | { |
2265 | getEmitter()->emitIns_S_I(ins_Store(dstType), sizeAttr, varNum, offs, (int)imm); |
2266 | } |
2267 | #elif defined(_TARGET_ARMARCH_) |
2268 | // Load imm into a register |
2269 | regNumber immReg = regToUse; |
2270 | assert(regToUse != REG_NA); |
2271 | instGen_Set_Reg_To_Imm(sizeAttr, immReg, (ssize_t)imm); |
2272 | instGen_Store_Reg_Into_Lcl(dstType, immReg, varNum, offs); |
2273 | if (EA_IS_RELOC(sizeAttr)) |
2274 | { |
2275 | regSet.verifyRegUsed(immReg); |
2276 | } |
2277 | #else // _TARGET_* |
2278 | #error "Unknown _TARGET_" |
2279 | #endif // _TARGET_* |
2280 | } |
2281 | |
2282 | /*****************************************************************************/ |
2283 | /*****************************************************************************/ |
2284 | /*****************************************************************************/ |
2285 | |