codegen: improve floating point comparisons on loongarch, mips, parisc
[ajla.git] / cg-util.inc
blob49e49d58ec9afa3444b040f04fde6e66de7ed247
1 /*
2  * Copyright (C) 2024 Mikulas Patocka
3  *
4  * This file is part of Ajla.
5  *
6  * Ajla is free software: you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * Ajla. If not, see <https://www.gnu.org/licenses/>.
17  */
19 static bool attr_w gen_mov(struct codegen_context *ctx, unsigned size, unsigned dest, unsigned src)
21         if (dest == src && (size == OP_SIZE_NATIVE || reg_is_fp(dest)))
22                 return true;
24         gen_insn(INSN_MOV, size, 0, 0);
25         gen_one(dest);
26         gen_one(src);
28         return true;
31 static bool attr_w gen_sanitize_returned_pointer(struct codegen_context attr_unused *ctx, unsigned attr_unused reg)
33 #if defined(ARCH_X86_X32)
34         g(gen_mov(ctx, OP_SIZE_ADDRESS, reg, reg));
35 #endif
36         return true;
39 static bool alu_is_commutative(unsigned alu)
41         return alu == ALU_ADD || alu == ALU_OR || alu == ALU_AND || alu == ALU_XOR || alu == ALU_MUL || alu == ALU_UMULH || alu == ALU_SMULH;
44 static bool attr_w gen_3address_alu(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2, unsigned writes_flags)
46         if (unlikely(dest == src2) && alu_is_commutative(alu)) {
47                 unsigned swap = src1;
48                 src1 = src2;
49                 src2 = swap;
50         }
51         if (!ARCH_IS_3ADDRESS(alu, writes_flags) && unlikely(dest == src2) && unlikely(dest != src1)) {
52                 internal(file_line, "gen_3address_alu: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
53         }
54         if (!ARCH_IS_3ADDRESS(alu, writes_flags) && dest != src1) {
55                 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src1));
57                 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(alu, false) | writes_flags);
58                 gen_one(dest);
59                 gen_one(dest);
60                 gen_one(src2);
62                 return true;
63         }
64         gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(alu, false) | writes_flags);
65         gen_one(dest);
66         gen_one(src1);
67         gen_one(src2);
68         return true;
71 static bool attr_w gen_3address_alu_imm(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, int64_t imm, unsigned writes_flags)
73         unsigned purpose =
74                 alu == ALU_ADD ? IMM_PURPOSE_ADD :
75                 alu == ALU_SUB ? IMM_PURPOSE_SUB :
76                 alu == ALU_MUL ? IMM_PURPOSE_MUL :
77                 alu == ALU_UMULH ? IMM_PURPOSE_MUL :
78                 alu == ALU_SMULH ? IMM_PURPOSE_MUL :
79                 alu == ALU_ANDN ? IMM_PURPOSE_ANDN :
80                 alu == ALU_AND ? IMM_PURPOSE_AND :
81                 alu == ALU_OR ? IMM_PURPOSE_OR :
82                 alu == ALU_XOR ? IMM_PURPOSE_XOR :
83                 alu == ALU_EXTBL ? IMM_PURPOSE_OR :
84                 alu == ALU_EXTWL ? IMM_PURPOSE_OR :
85                 alu == ALU_EXTLL ? IMM_PURPOSE_OR :
86                 alu == ALU_EXTLH ? IMM_PURPOSE_OR :
87                 alu == ALU_INSBL ? IMM_PURPOSE_OR :
88                 alu == ALU_MSKBL ? IMM_PURPOSE_OR :
89                 alu == ALU_ZAP ? IMM_PURPOSE_ANDN :
90                 alu == ALU_ZAPNOT ? IMM_PURPOSE_AND :
91                 -1U;
92         if (unlikely(purpose == -1U))
93                 internal(file_line, "gen_3address_alu_imm: invalid parameters: size %u, alu %u, dest %u, src %u, imm %"PRIxMAX"", size, alu, dest, src, (uintmax_t)imm);
94         if (!ARCH_IS_3ADDRESS_IMM(alu, writes_flags) && dest != src) {
95                 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
97                 g(gen_imm(ctx, imm, purpose, i_size(OP_SIZE_ADDRESS)));
98                 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(alu, is_imm()) | writes_flags);
99                 gen_one(dest);
100                 gen_one(dest);
101                 gen_imm_offset();
103                 return true;
104         }
105         g(gen_imm(ctx, imm, purpose, i_size(OP_SIZE_ADDRESS)));
106         gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(alu, is_imm()) | writes_flags);
107         gen_one(dest);
108         gen_one(src);
109         gen_imm_offset();
111         return true;
114 static bool attr_w attr_unused gen_3address_rot(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2)
116 #ifdef ARCH_X86
117         if (dest == src1 && src2 == R_CX) {
118                 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, 1);
119                 gen_one(dest);
120                 gen_one(src1);
121                 gen_one(src2);
123                 return true;
124         }
125 #endif
126         if (!ARCH_IS_3ADDRESS_ROT(alu, size) && dest != src1) {
127                 if (unlikely(dest == src2))
128                         internal(file_line, "gen_3address_rot: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
130                 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src1));
132                 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, false));
133                 gen_one(dest);
134                 gen_one(dest);
135                 gen_one(src2);
137                 return true;
138         }
139         gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, false));
140         gen_one(dest);
141         gen_one(src1);
142         gen_one(src2);
144         return true;
147 static bool attr_w gen_3address_rot_imm(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, int64_t imm, unsigned writes_flags)
149         if (!ARCH_IS_3ADDRESS_ROT_IMM(alu) && dest != src) {
150                 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
152                 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, true) | writes_flags);
153                 gen_one(dest);
154                 gen_one(dest);
155                 gen_one(ARG_IMM);
156                 gen_eight(imm);
158                 return true;
159         }
160         gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, true) | writes_flags);
161         gen_one(dest);
162         gen_one(src);
163         gen_one(ARG_IMM);
164         gen_eight(imm);
166         return true;
169 static bool attr_w gen_2address_alu1(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, unsigned writes_flags)
171         if (!ARCH_IS_2ADDRESS(alu) && dest != src) {
172                 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
174                 gen_insn(INSN_ALU1 + ARCH_PARTIAL_ALU(size), size, alu, ALU1_WRITES_FLAGS(alu) | writes_flags);
175                 gen_one(dest);
176                 gen_one(dest);
178                 return true;
179         }
180         gen_insn(INSN_ALU1 + ARCH_PARTIAL_ALU(size), size, alu, ALU1_WRITES_FLAGS(alu) | writes_flags);
181         gen_one(dest);
182         gen_one(src);
184         return true;
187 static bool attr_w gen_3address_fp_alu(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2)
189         if (!ARCH_IS_3ADDRESS_FP && unlikely(dest == src2) && unlikely(dest != src1)) {
190                 internal(file_line, "gen_3address_fp_alu: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
191         }
192         if (!ARCH_IS_3ADDRESS_FP && dest != src1) {
193                 g(gen_mov(ctx, size, dest, src1));
195                 gen_insn(INSN_FP_ALU, size, alu, 0);
196                 gen_one(dest);
197                 gen_one(dest);
198                 gen_one(src2);
200                 return true;
201         }
202         gen_insn(INSN_FP_ALU, size, alu, 0);
203         gen_one(dest);
204         gen_one(src1);
205         gen_one(src2);
207         return true;
211 static bool attr_w attr_unused gen_load_two(struct codegen_context *ctx, unsigned dest, unsigned src, int64_t offset)
213         if (!ARCH_HAS_BWX) {
214                 if (!(offset & 7)) {
215                         g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
216                         gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
217                         gen_one(dest);
218                         gen_address_offset();
220                         g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTWL, dest, dest, src, 0));
221                 } else {
222                         g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
223                         gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, is_imm()));
224                         gen_one(R_OFFSET_IMM);
225                         gen_one(src);
226                         gen_imm_offset();
228                         gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
229                         gen_one(dest);
230                         gen_one(ARG_ADDRESS_1);
231                         gen_one(R_OFFSET_IMM);
232                         gen_eight(0);
234                         g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTWL, dest, dest, R_OFFSET_IMM, 0));
235                 }
236 #if defined(ARCH_S390)
237         } else if (!cpu_test_feature(CPU_FEATURE_extended_imm)) {
238                 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_2));
239                 gen_insn(INSN_MOVSX, OP_SIZE_2, 0, 0);
240                 gen_one(dest);
241                 gen_address_offset();
243                 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_AND, dest, dest, 0xffff, 0));
244 #endif
245         } else {
246                 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_2));
247                 gen_insn(INSN_MOV, OP_SIZE_2, 0, 0);
248                 gen_one(dest);
249                 gen_address_offset();
250         }
251         return true;
254 static bool attr_w gen_load_code_32(struct codegen_context *ctx, unsigned dest, unsigned src, int64_t offset)
256 #if ARG_MODE_N == 3 && defined(ARCH_ALPHA) && !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
257         if (!ARCH_HAS_BWX && UNALIGNED_TRAP) {
258                 if (offset & 7) {
259                         g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_ADD, R_OFFSET_IMM, src, offset, 0));
260                         src = R_OFFSET_IMM;
261                         offset = 0;
262                 }
263                 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
264                 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
265                 gen_one(dest);
266                 gen_address_offset();
268                 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTLL, dest, dest, src, 0));
270                 g(gen_address(ctx, src, offset + 3, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
271                 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
272                 gen_one(R_CONST_IMM);
273                 gen_address_offset();
275                 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTLH, R_CONST_IMM, R_CONST_IMM, src, 0));
277                 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_OR, dest, dest, R_CONST_IMM, 0));
279                 return true;
280         }
281 #endif
282 #if ARG_MODE_N == 3 && defined(ARCH_MIPS) && !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
283         if (!MIPS_R6 && UNALIGNED_TRAP) {
284                 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
285                 gen_insn(INSN_MOV_LR, OP_SIZE_4, !CODE_ENDIAN, 0);
286                 gen_one(dest);
287                 gen_one(dest);
288                 gen_address_offset();
290                 g(gen_address(ctx, src, offset + 3, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
291                 gen_insn(INSN_MOV_LR, OP_SIZE_4, CODE_ENDIAN, 0);
292                 gen_one(dest);
293                 gen_one(dest);
294                 gen_address_offset();
296                 return true;
297         }
298 #endif
299 #if ARG_MODE_N == 3
300 #if !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
301         if (UNALIGNED_TRAP)
302 #endif
303         {
304                 g(gen_load_two(ctx, dest, src, offset));
305                 g(gen_load_two(ctx, R_CONST_IMM, src, offset + 2));
306 #if CODE_ENDIAN
307                 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, dest, 16, false));
308 #else
309                 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, R_CONST_IMM, R_CONST_IMM, 16, false));
310 #endif
311                 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_OR, dest, dest, R_CONST_IMM, 0));
312                 return true;
313         }
314 #endif
315         g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, ARG_MODE_N - 1));
316         gen_insn(INSN_MOV, ARG_MODE_N - 1, 0, 0);
317         gen_one(dest);
318         gen_address_offset();
319         return true;
322 static bool attr_w attr_unused gen_cmp_dest_reg(struct codegen_context *ctx, unsigned attr_unused size, unsigned reg1, unsigned reg2, unsigned reg_dest, int64_t imm, unsigned cond)
324         unsigned neg_result = false;
326 #if defined(ARCH_ALPHA)
327         if (cond == COND_NE) {
328                 if (reg2 == (unsigned)-1)
329                         g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
330                 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_E, 0);
331                 gen_one(reg_dest);
332                 gen_one(reg1);
333                 if (reg2 == (unsigned)-1)
334                         gen_imm_offset();
335                 else
336                         gen_one(reg2);
337                 neg_result = true;
338                 goto done;
339         }
340 #endif
341 #if defined(ARCH_LOONGARCH64) || defined(ARCH_MIPS) || defined(ARCH_RISCV64)
342         if (cond == COND_E || cond == COND_NE) {
343                 unsigned rx;
344                 if (reg2 == (unsigned)-1 && !imm) {
345                         rx = reg1;
346                         goto skip_xor;
347                 }
348                 if (reg2 == (unsigned)-1)
349                         g(gen_imm(ctx, imm, IMM_PURPOSE_XOR, i_size(size)));
350                 gen_insn(INSN_ALU, i_size(size), ALU_XOR, ALU_WRITES_FLAGS(ALU_XOR, reg2 == (unsigned)-1 ? is_imm() : false));
351                 gen_one(reg_dest);
352                 gen_one(reg1);
353                 if (reg2 == (unsigned)-1)
354                         gen_imm_offset();
355                 else
356                         gen_one(reg2);
357                 rx = reg_dest;
358 skip_xor:
359                 if (cond == COND_E) {
360                         g(gen_imm(ctx, 1, IMM_PURPOSE_CMP, i_size_cmp(size)));
361                         gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_B, 0);
362                         gen_one(reg_dest);
363                         gen_one(rx);
364                         gen_imm_offset();
365                 } else {
366                         gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_B, 0);
367                         gen_one(reg_dest);
368                         gen_one(ARG_IMM);
369                         gen_eight(0);
370                         gen_one(rx);
371                 }
372                 goto done;
373         }
374         if (cond == COND_GE || cond == COND_LE || cond == COND_AE || cond == COND_BE) {
375                 cond ^= 1;
376                 neg_result = true;
377         }
378 #endif
379 #if defined(ARCH_IA64)
380         if (reg2 == (unsigned)-1)
381                 g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
382         gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), cond, 0);
383         gen_one(R_CMP_RESULT);
384         gen_one(reg1);
385         if (reg2 == (unsigned)-1)
386                 gen_imm_offset();
387         else
388                 gen_one(reg2);
390         g(gen_mov(ctx, OP_SIZE_NATIVE, reg_dest, R_CMP_RESULT));
392         goto done;
393 #endif
394         if (reg2 == (unsigned)-1)
395                 g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
396         gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), cond, 0);
397         gen_one(reg_dest);
398         gen_one(reg1);
399         if (reg2 == (unsigned)-1)
400                 gen_imm_offset();
401         else
402                 gen_one(reg2);
404         goto done;
405 done:
406         if (neg_result)
407                 g(gen_3address_alu_imm(ctx, i_size(size), ALU_XOR, reg_dest, reg_dest, 1, 0));
409         return true;
412 static bool attr_w gen_cmp_test_jmp(struct codegen_context *ctx, unsigned insn, unsigned op_size, unsigned reg1, unsigned reg2, unsigned cond, uint32_t label)
414         bool arch_use_flags = ARCH_HAS_FLAGS;
415 #if defined(ARCH_ARM64)
416         if (insn == INSN_TEST && reg1 == reg2 && (cond == COND_E || cond == COND_NE))
417                 arch_use_flags = false;
418 #endif
419 #if defined(ARCH_SPARC)
420         if (insn == INSN_TEST && reg1 == reg2)
421                 arch_use_flags = false;
422 #endif
423         if (arch_use_flags) {
424                 if (COND_IS_LOGICAL(cond)) {
425                         gen_insn(insn, op_size, 0, 2);
426                         gen_one(reg1);
427                         gen_one(reg2);
429                         gen_insn(INSN_JMP_COND_LOGICAL, op_size, cond, 0);
430                         gen_four(label);
432                         return true;
433                 }
435                 gen_insn(insn, op_size, 0, 1);
436                 gen_one(reg1);
437                 gen_one(reg2);
439 #if defined(ARCH_POWER) || defined(ARCH_S390)
440                 if (insn == INSN_TEST) {
441                         if (cond == COND_S)
442                                 cond = COND_L;
443                         if (cond == COND_NS)
444                                 cond = COND_GE;
445                 }
446 #endif
447                 gen_insn(INSN_JMP_COND, op_size, cond, 0);
448                 gen_four(label);
449         } else {
450                 if (insn == INSN_CMP) {
451 #if defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_RISCV64) || (defined(ARCH_MIPS) && MIPS_R6)
452                         gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
453                         gen_one(reg1);
454                         gen_one(reg2);
455                         gen_four(label);
456                         return true;
457 #else
458 #ifdef R_CMP_RESULT
459                         unsigned jmp_cond = COND_NE;
460 #if defined(ARCH_MIPS)
461                         if (cond == COND_E || cond == COND_NE) {
462                                 gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
463                                 gen_one(reg1);
464                                 gen_one(reg2);
465                                 gen_four(label);
466                                 return true;
467                         }
468                         if (cond == COND_AE || cond == COND_BE || cond == COND_LE || cond == COND_GE) {
469                                 cond ^= 1;
470                                 jmp_cond ^= 1;
471                         }
472 #endif
473 #if defined(ARCH_ALPHA)
474                         if (cond == COND_NE) {
475                                 g(gen_3address_alu(ctx, op_size, ALU_XOR, R_CMP_RESULT, reg1, reg2, 0));
476                         } else
477 #endif
478                         {
479                                 gen_insn(INSN_CMP_DEST_REG, op_size, cond, 0);
480                                 gen_one(R_CMP_RESULT);
481                                 gen_one(reg1);
482                                 gen_one(reg2);
483                         }
485                         gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, jmp_cond, 0);
486                         gen_one(R_CMP_RESULT);
487                         gen_four(label);
488 #else
489                         internal(file_line, "gen_cmp_test_jmp: R_CMP_RESULT not defined");
490 #endif
491 #endif
492                 } else if (insn == INSN_TEST) {
493                         if (reg1 != reg2) {
494                                 internal(file_line, "gen_cmp_test_jmp: INSN_TEST with two distinct registers is unsupported");
495                         }
496 #if defined(ARCH_IA64)
497                         if (cond == COND_S)
498                                 cond = COND_L;
499                         if (cond == COND_NS)
500                                 cond = COND_GE;
501                         g(gen_imm(ctx, 0, IMM_PURPOSE_CMP, OP_SIZE_NATIVE));
502                         gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
503                         gen_one(R_CMP_RESULT);
504                         gen_one(reg1);
505                         gen_imm_offset();
507                         reg1 = R_CMP_RESULT;
508                         cond = COND_NE;
509 #endif
510                         gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
511                         gen_one(reg1);
512                         gen_four(label);
513                 }
514         }
515         return true;
518 static bool attr_w gen_cmp_test_imm_jmp(struct codegen_context *ctx, unsigned insn, unsigned attr_unused op_size, unsigned reg1, int64_t value, unsigned cond, uint32_t label)
520         if (insn == INSN_TEST && (cond == COND_E || cond == COND_NE) && is_power_of_2((uint64_t)value)) {
521 #ifdef HAVE_BUILTIN_CTZ
522                 unsigned attr_unused bit = __builtin_ctzll(value);
523 #else
524                 unsigned attr_unused bit = 0;
525                 uint64_t v = value;
526                 while ((v = v >> 1))
527                         bit++;
528 #endif
529 #if defined(ARCH_ALPHA) || defined(ARCH_PARISC)
530                 if (value == 1 && (cond == COND_E || cond == COND_NE)) {
531                         gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond == COND_E ? COND_BLBC : COND_BLBS, 0);
532                         gen_one(reg1);
533                         gen_four(label);
534                         return true;
535                 }
536 #endif
537 #if defined(ARCH_ARM64) || defined(ARCH_PARISC)
538                 gen_insn(INSN_JMP_REG_BIT, OP_SIZE_NATIVE, bit | ((cond == COND_NE) << 6), 0);
539                 gen_one(reg1);
540                 gen_four(label);
542                 return true;
543 #endif
544 #if defined(ARCH_POWER)
545                 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, R_CONST_IMM, reg1, (8U << OP_SIZE_NATIVE) - 1 - bit, true));
547                 gen_insn(INSN_JMP_COND, OP_SIZE_NATIVE, cond == COND_E ? COND_GE : COND_L, 0);
548                 gen_four(label);
550                 return true;
551 #endif
552 #if defined(ARCH_IA64)
553                 gen_insn(INSN_TEST_DEST_REG, OP_SIZE_NATIVE, bit | ((cond == COND_NE) << 6), 0);
554                 gen_one(R_CMP_RESULT);
555                 gen_one(reg1);
557                 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, COND_NE, 0);
558                 gen_one(R_CMP_RESULT);
559                 gen_four(label);
561                 return true;
562 #endif
563 #if defined(R_CMP_RESULT)
564                 if (!is_direct_const(1ULL << bit, IMM_PURPOSE_AND, OP_SIZE_NATIVE) && ARCH_HAS_BTX(BTX_BTEXT, OP_SIZE_NATIVE, true)) {
565                         gen_insn(INSN_BTX, OP_SIZE_NATIVE, BTX_BTEXT, 0);
566                         gen_one(R_CMP_RESULT);
567                         gen_one(reg1);
568                         gen_one(ARG_IMM);
569                         gen_eight(bit);
571                         gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
572                         gen_one(R_CMP_RESULT);
573                         gen_four(label);
575                         return true;
576                 }
577 #endif
578         }
579 #if ARCH_HAS_FLAGS
580         if (unlikely(insn == INSN_CMP) && COND_IS_LOGICAL(cond)) {
581                 g(gen_imm(ctx, value, IMM_PURPOSE_CMP_LOGICAL, op_size));
582                 gen_insn(insn, op_size, 0, 2);
583                 gen_one(reg1);
584                 gen_imm_offset();
586                 gen_insn(INSN_JMP_COND_LOGICAL, op_size, cond, 0);
587                 gen_four(label);
589                 return true;
590         }
591         g(gen_imm(ctx, value, insn == INSN_CMP ? IMM_PURPOSE_CMP : IMM_PURPOSE_TEST, op_size));
592         gen_insn(insn, op_size, 0, 1);
593         gen_one(reg1);
594         gen_imm_offset();
596         gen_insn(INSN_JMP_COND, op_size, cond, 0);
597         gen_four(label);
598 #else
599         if (insn == INSN_CMP) {
600 #if defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_RISCV64)
601                 g(gen_imm(ctx, value, IMM_PURPOSE_JMP_2REGS, op_size));
602 #if defined(ARCH_PARISC)
603                 gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
604 #else
605                 gen_insn(INSN_JMP_2REGS, i_size_cmp(op_size), cond, 0);
606 #endif
607                 gen_one(reg1);
608                 gen_imm_offset();
609                 gen_four(label);
610                 return true;
611 #else
612                 unsigned final_cond = COND_NE;
613 #if defined(ARCH_ALPHA)
614                 if (cond == COND_AE || cond == COND_A || cond == COND_GE || cond == COND_G) {
615                         cond ^= 1;
616                         final_cond ^= 1;
617                         goto gen_const;
618                 } else if (cond == COND_NE) {
619                         g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_XOR, R_CMP_RESULT, reg1, value, 0));
620                 } else
621 gen_const:
622 #endif
623 #if defined(ARCH_MIPS)
624                 if (cond == COND_E || cond == COND_NE) {
625                         g(gen_load_constant(ctx, R_CONST_IMM, value));
626                         gen_insn(INSN_JMP_2REGS, OP_SIZE_NATIVE, cond, 0);
627                         gen_one(reg1);
628                         gen_one(R_CONST_IMM);
629                         gen_four(label);
630                         return true;
631                 }
632                 if (cond == COND_AE || cond == COND_BE || cond == COND_LE || cond == COND_GE) {
633                         cond ^= 1;
634                         final_cond ^= 1;
635                 }
636                 if (cond == COND_A || cond == COND_G) {
637                         g(gen_load_constant(ctx, R_CONST_IMM, value));
638                         gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
639                         gen_one(R_CMP_RESULT);
640                         gen_one(reg1);
641                         gen_one(R_CONST_IMM);
642                 } else
643 #endif
644                 {
645                         g(gen_imm(ctx, value, IMM_PURPOSE_CMP, OP_SIZE_NATIVE));
646                         gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
647                         gen_one(R_CMP_RESULT);
648                         gen_one(reg1);
649                         gen_imm_offset();
650                 }
652                 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, final_cond, 0);
653                 gen_one(R_CMP_RESULT);
654                 gen_four(label);
655 #endif
656         } else if (insn == INSN_TEST) {
657 #if defined(ARCH_IA64)
658                 internal(file_line, "gen_cmp_test_imm_jmp: value %"PRIxMAX" not supported", (uintmax_t)value);
659 #endif
660                 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_AND, R_CMP_RESULT, reg1, value, 0));
662                 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
663                 gen_one(R_CMP_RESULT);
664                 gen_four(label);
665         } else {
666                 internal(file_line, "gen_cmp_test_imm_jmp: invalid insn");
667         }
668 #endif
669         return true;
672 static bool attr_w gen_jmp_on_zero(struct codegen_context *ctx, unsigned attr_unused op_size, unsigned reg, unsigned cond, uint32_t label)
674         bool jmp_reg = false;
675 #if defined(ARCH_ALPHA) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64) || defined(ARCH_RISCV64)
676         jmp_reg = true;
677 #endif
678 #if defined(ARCH_SPARC)
679         jmp_reg |= SPARC_9;
680 #endif
681         if (jmp_reg) {
682                 gen_insn(INSN_JMP_REG, i_size(op_size), cond, 0);
683                 gen_one(reg);
684                 gen_four(label);
686                 return true;
687         }
688         g(gen_cmp_test_jmp(ctx, INSN_TEST, i_size(op_size), reg, reg, cond, label));
690         return true;
693 static bool attr_w gen_jmp_if_negative(struct codegen_context *ctx, unsigned reg, uint32_t label)
695 #if defined(ARCH_ARM64) || defined(ARCH_PARISC)
696         gen_insn(INSN_JMP_REG_BIT, OP_SIZE_NATIVE, (INT_DEFAULT_BITS - 1) | ((uint32_t)1 << 6), 0);
697         gen_one(reg);
698         gen_four(label);
699 #else
700         g(gen_jmp_on_zero(ctx, OP_SIZE_INT, reg, COND_S, label));
701 #endif
702         return true;
705 #if defined(ARCH_X86)
706 static bool attr_w gen_cmov(struct codegen_context *ctx, unsigned op_size, unsigned cond, unsigned reg, uint32_t *label)
708         if (unlikely(op_size < OP_SIZE_4))
709                 internal(file_line, "gen_cmov: unsupported operand size");
710         if (likely(cpu_test_feature(CPU_FEATURE_cmov))) {
711                 gen_insn(INSN_CMOV, op_size, cond, 0);
712                 gen_one(reg);
713                 gen_one(reg);
714                 *label = 0;
715         } else {
716                 *label = alloc_label(ctx);
717                 if (unlikely(!*label))
718                         return false;
719                 gen_insn(INSN_JMP_COND, op_size, cond ^ 1, 0);
720                 gen_four(*label);
721                 gen_insn(INSN_MOV, op_size, 0, 0);
722                 gen_one(reg);
723         }
724         return true;
726 #endif
728 enum extend {
729         zero_x,
730         sign_x,
731         native,
732         garbage,
735 static bool attr_w gen_extend(struct codegen_context *ctx, unsigned op_size, enum extend ex, unsigned dest, unsigned src)
737         unsigned attr_unused shift;
738         if (ex == native)
739                 ex = ARCH_PREFERS_SX(op_size) ? sign_x : zero_x;
740         ajla_assert_lo(ex == zero_x || ex == sign_x, (file_line, "gen_extend: invalid mode %u", (unsigned)ex));
741         if (unlikely(op_size == OP_SIZE_NATIVE)) {
742                 g(gen_mov(ctx, op_size, dest, src));
743                 return true;
744         }
745         if (OP_SIZE_NATIVE == OP_SIZE_4) {
746                 shift = op_size == OP_SIZE_1 ? 24 : 16;
747         } else if (OP_SIZE_NATIVE == OP_SIZE_8) {
748                 shift = op_size == OP_SIZE_1 ? 56 : op_size == OP_SIZE_2 ? 48 : 32;
749         } else {
750                 internal(file_line, "gen_extend: invalid OP_SIZE_NATIVE");
751         }
752 #if defined(ARCH_ARM) || defined(ARCH_IA64) || defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_X86)
753 #if defined(ARCH_ARM32)
754         if (unlikely(!cpu_test_feature(CPU_FEATURE_armv6)))
755                 goto default_extend;
756 #endif
757         gen_insn(ex == sign_x ? INSN_MOVSX : INSN_MOV, op_size, 0, 0);
758         gen_one(dest);
759         gen_one(src);
760         return true;
761 #endif
762 #if defined(ARCH_POWER)
763         if (ex == zero_x || op_size == OP_SIZE_2 || cpu_test_feature(CPU_FEATURE_ppc)) {
764                 gen_insn(ex == sign_x ? INSN_MOVSX : INSN_MOV, op_size, 0, 0);
765                 gen_one(dest);
766                 gen_one(src);
767                 return true;
768         }
769 #endif
770 #if defined(ARCH_ALPHA)
771         if (ex == zero_x) {
772                 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_ZAPNOT, dest, src, op_size == OP_SIZE_1 ? 0x1 : op_size == OP_SIZE_2 ? 0x3 : 0xf, 0));
773                 return true;
774         } else if (op_size == OP_SIZE_4 || ARCH_HAS_BWX) {
775                 gen_insn(INSN_MOVSX, op_size, 0, 0);
776                 gen_one(dest);
777                 gen_one(src);
778                 return true;
779         }
780 #endif
781 #if defined(ARCH_MIPS)
782         if (ex == sign_x && shift == 32) {
783                 g(gen_3address_rot_imm(ctx, OP_SIZE_4, ROT_SHL, dest, src, 0, 0));
784                 return true;
785         }
786         if (ex == sign_x && MIPS_HAS_ROT) {
787                 gen_insn(INSN_MOVSX, op_size, 0, 0);
788                 gen_one(dest);
789                 gen_one(src);
790                 return true;
791         }
792 #endif
793 #if defined(ARCH_S390)
794         if (((op_size == OP_SIZE_1 || op_size == OP_SIZE_2) && cpu_test_feature(CPU_FEATURE_extended_imm)) || op_size == OP_SIZE_4) {
795                 gen_insn(ex == zero_x ? INSN_MOV : INSN_MOVSX, op_size, 0, 0);
796                 gen_one(dest);
797                 gen_one(src);
798                 return true;
799         }
800 #endif
801 #if defined(ARCH_SPARC)
802         if (shift == 32) {
803                 g(gen_3address_rot_imm(ctx, OP_SIZE_4, ex == sign_x ? ROT_SAR : ROT_SHR, dest, src, 0, 0));
804                 return true;
805         }
806 #endif
807 #if defined(ARCH_RISCV64)
808         if (ex == sign_x && (op_size == OP_SIZE_4 || likely(cpu_test_feature(CPU_FEATURE_zbb)))) {
809                 gen_insn(INSN_MOVSX, op_size, 0, 0);
810                 gen_one(dest);
811                 gen_one(src);
812                 return true;
813         }
814         if (ex == zero_x && ((op_size == OP_SIZE_1) ||
815                     (op_size == OP_SIZE_2 && likely(cpu_test_feature(CPU_FEATURE_zbb))) ||
816                     (op_size == OP_SIZE_4 && likely(cpu_test_feature(CPU_FEATURE_zba))))) {
817                 g(gen_mov(ctx, op_size, dest, src));
818                 return true;
819         }
820 #endif
821         goto default_extend;
822 default_extend:
823         if (ex == zero_x && op_size <= OP_SIZE_4) {
824                 int64_t cnst = (0x1ULL << (8U << op_size)) - 1;
825                 if (is_direct_const(cnst, IMM_PURPOSE_AND, OP_SIZE_NATIVE)) {
826                         g(gen_imm(ctx, 0xff, IMM_PURPOSE_AND, OP_SIZE_NATIVE));
827                         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_AND, ALU_WRITES_FLAGS(ALU_AND, is_imm()));
828                         gen_one(dest);
829                         gen_one(src);
830                         gen_imm_offset();
831                         return true;
832                 }
833         }
834         g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, src, shift, false));
835         g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ex == sign_x ? ROT_SAR : ROT_SHR, dest, dest, shift, false));
836         return true;
839 static bool attr_w gen_cmp_extended(struct codegen_context *ctx, unsigned cmp_op_size, unsigned sub_op_size, unsigned reg, unsigned attr_unused tmp_reg, uint32_t label_ovf)
841         if (unlikely(sub_op_size >= cmp_op_size))
842                 return true;
843 #if defined(ARCH_ARM64)
844         gen_insn(INSN_CMP, cmp_op_size, 0, 1);
845         gen_one(reg);
846         gen_one(ARG_EXTENDED_REGISTER);
847         gen_one(sub_op_size == OP_SIZE_1 ? ARG_EXTEND_SXTB : sub_op_size == OP_SIZE_2 ? ARG_EXTEND_SXTH : ARG_EXTEND_SXTW);
848         gen_one(reg);
850         gen_insn(INSN_JMP_COND, cmp_op_size, COND_NE, 0);
851         gen_four(label_ovf);
852 #else
853         g(gen_extend(ctx, sub_op_size, sign_x, tmp_reg, reg));
855         g(gen_cmp_test_jmp(ctx, INSN_CMP, cmp_op_size, reg, tmp_reg, COND_NE, label_ovf));
856 #endif
857         return true;
860 static bool attr_w gen_lea3(struct codegen_context *ctx, unsigned dest, unsigned base, unsigned shifted, unsigned shift, int64_t offset)
862 #if defined(ARCH_X86)
863         gen_insn(INSN_LEA3, i_size(OP_SIZE_ADDRESS), shift, 0);
864         gen_one(dest);
865         gen_one(base);
866         gen_one(shifted);
867         gen_one(ARG_IMM);
868         gen_eight(likely(imm_is_32bit(offset)) ? offset : 0);
870         if (unlikely(!imm_is_32bit(offset)))
871                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, dest, offset, 0));
873         return true;
874 #endif
875         if (ARCH_HAS_SHIFTED_ADD(shift)) {
876                 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, false));
877                 gen_one(dest);
878                 gen_one(base);
879                 gen_one(ARG_SHIFTED_REGISTER);
880                 gen_one(ARG_SHIFT_LSL | shift);
881                 gen_one(shifted);
883                 if (offset) {
884                         g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
885                         gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, is_imm()));
886                         gen_one(dest);
887                         gen_one(dest);
888                         gen_imm_offset();
889                 }
891                 return true;
892         }
894         g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, shifted, shift, false));
896         g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_ADD, dest, dest, base, 0));
898         if (offset)
899                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, dest, offset, 0));
900         return true;
903 #if !defined(POINTER_COMPRESSION)
904 #define gen_pointer_compression(base)           do { } while (0)
905 #define gen_address_offset_compressed()         gen_address_offset()
906 #elif defined(ARCH_X86)
907 #define gen_pointer_compression(base)           do { } while (0)
908 #define gen_address_offset_compressed()                                 \
909 do {                                                                    \
910         if (likely(!ctx->offset_reg)) {                                 \
911                 gen_one(ARG_ADDRESS_1 + POINTER_COMPRESSION);           \
912                 gen_one(ctx->base_reg);                                 \
913                 gen_eight(ctx->offset_imm);                             \
914         } else {                                                        \
915                 gen_one(ARG_ADDRESS_2 + POINTER_COMPRESSION);           \
916                 gen_one(R_OFFSET_IMM);                                  \
917                 gen_one(ctx->base_reg);                                 \
918                 gen_eight(0);                                           \
919         }                                                               \
920 } while (0)
921 #else
922 #define gen_pointer_compression(base)                                   \
923 do {                                                                    \
924         if (ARCH_PREFERS_SX(OP_SIZE_4)) {                               \
925                 g(gen_extend(ctx, OP_SIZE_4, zero_x, base, base));      \
926         }                                                               \
927         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, base, base, POINTER_COMPRESSION, 0));\
928 } while (0)
929 #define gen_address_offset_compressed()         gen_address_offset()
930 #endif