rework the verifier to prepare for loop cutting
[ajla.git] / cg-frame.inc
blob37fe4e8390f29f0e99c238b1b413a948285a60f5
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 #define frame_offs(x)   ((ssize_t)offsetof(struct frame_struct, x) - (ssize_t)frame_offset)
21 #if defined(C_LITTLE_ENDIAN)
22 #define lo_word(size)           (0)
23 #define hi_word(size)           ((size_t)1 << (size))
24 #elif defined(C_BIG_ENDIAN)
25 #define lo_word(size)           ((size_t)1 << (size))
26 #define hi_word(size)           (0)
27 #else
28 endian not defined
29 #endif
32 static bool attr_w gen_frame_load_raw(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, int64_t offset, bool dual, unsigned reg);
33 static bool attr_w gen_frame_store_raw(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, unsigned reg);
36 static const struct type *get_type_of_local(struct codegen_context *ctx, frame_t pos)
38         const struct type *t;
39         const struct data *function = ctx->fn;
40         t = da(function,function)->local_variables[pos].type;
41         if (t)
42                 TYPE_TAG_VALIDATE(t->tag);
43         return t;
46 static unsigned real_type_to_op_size(unsigned real_type)
48         switch (real_type) {
49                 case 0: return OP_SIZE_2;
50                 case 1: return OP_SIZE_4;
51                 case 2: return OP_SIZE_8;
52                 case 3: return OP_SIZE_10;
53                 case 4: return OP_SIZE_16;
54                 default:
55                         internal(file_line, "real_type_to_op_size: invalid type %u", real_type);
56                         return 0;
57         }
60 static unsigned spill_size(const struct type *t)
62         if (!TYPE_TAG_IS_BUILTIN(t->tag)) {
63                 return OP_SIZE_SLOT;
64         } else if (TYPE_TAG_IS_REAL(t->tag)) {
65                 return real_type_to_op_size(TYPE_TAG_IDX_REAL(t->tag));
66         } else {
67                 return log_2(t->size);
68         }
71 static bool attr_w spill(struct codegen_context *ctx, frame_t v)
73         const struct type *t = get_type_of_local(ctx, v);
74         g(gen_frame_store_raw(ctx, spill_size(t), v, 0, ctx->registers[v]));
75         return true;
78 static bool attr_w unspill(struct codegen_context *ctx, frame_t v)
80         const struct type *t = get_type_of_local(ctx, v);
81         enum extend ex = garbage;
82         if (t->tag == TYPE_TAG_flat_option)
83                 ex = zero;
84         else if (!TYPE_IS_FLAT(t))
85                 ex = native;
86         g(gen_frame_load_raw(ctx, spill_size(t), ex, v, 0, false, ctx->registers[v]));
87         return true;
91 static bool attr_w gen_frame_address(struct codegen_context *ctx, frame_t slot, int64_t offset, unsigned reg)
93         offset += (size_t)slot * slot_size;
94         g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, reg, R_FRAME, offset, 0));
95         return true;
98 static int64_t get_constant(unsigned size, enum extend ex, frame_t slot, int64_t offset, bool dual)
100         if (ex == garbage)
101                 ex = sign_x;
102         if (ex == native)
103                 ex = ARCH_PREFERS_SX(size) ? sign_x : zero_x;
104         if (!dual) {
105                 int64_t c;
106                 if (unlikely(offset != 0))
107                         goto crash;
108                 c = frame_t_get_const(slot);
109                 if (ex == zero_x) {
110                         if (size == OP_SIZE_1) c &= 0xFFUL;
111                         if (size == OP_SIZE_2) c &= 0xFFFFUL;
112                         if (size == OP_SIZE_4) c &= 0xFFFFFFFFUL;
113                 }
114                 return c;
115         }
116         if (offset == (int64_t)lo_word(size))
117                 return frame_t_get_const(slot);
118         if (offset == (int64_t)hi_word(size))
119                 return frame_t_get_const(slot) < 0 ? -1 : 0;
120 crash:
121         internal(file_line, "get_constant: invalid offset %"PRIdMAX"", (intmax_t)offset);
122         return 0;
125 static bool attr_w gen_frame_load_raw(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, int64_t offset, bool dual, unsigned reg)
127         int64_t x_offset;
128         if (frame_t_is_const(slot)) {
129                 g(gen_load_constant(ctx, reg, get_constant(size, ex, slot, offset, dual)));
130                 return true;
131         }
132         if (ex == garbage || ex == native) {
133                 if (!reg_is_fp(reg))
134                         ex = ARCH_PREFERS_SX(size) ? sign_x : zero_x;
135                 else
136                         ex = zero_x;
137         }
138         x_offset = offset + (size_t)slot * slot_size;
139         if (!ARCH_HAS_BWX && size < OP_SIZE_4) {
140                 g(gen_address(ctx, R_FRAME, x_offset, reg_is_fp(reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_4));
141                 gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
142                 gen_one(reg);
143                 gen_address_offset();
145                 g(gen_extend(ctx, size, ex, reg, reg));
147                 return true;
148         }
149 #if defined(ARCH_ALPHA)
150         if (size < OP_SIZE_4) {
151                 g(gen_address(ctx, R_FRAME, x_offset, reg_is_fp(reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : IMM_PURPOSE_LDR_OFFSET, size));
152                 gen_insn(INSN_MOV, size, 0, 0);
153                 gen_one(reg);
154                 gen_address_offset();
156                 if (ex != zero_x)
157                         g(gen_extend(ctx, size, ex, reg, reg));
159                 return true;
160         }
161         if (size == OP_SIZE_4 && !reg_is_fp(reg) && ex == zero_x) {
162                 g(gen_frame_load_raw(ctx, size, sign_x, slot, offset, false, reg));
163                 g(gen_extend(ctx, size, ex, reg, reg));
165                 return true;
166         }
167 #endif
168 #if defined(ARCH_MIPS)
169         if (reg_is_fp(reg) && size == OP_SIZE_8 && !MIPS_HAS_LS_DOUBLE) {
170 #if defined(C_LITTLE_ENDIAN)
171                 g(gen_frame_load_raw(ctx, OP_SIZE_4, zero_x, slot, offset, true, reg));
172                 g(gen_frame_load_raw(ctx, OP_SIZE_4, zero_x, slot, offset + 4, true, reg + 1));
173 #else
174                 g(gen_frame_load_raw(ctx, OP_SIZE_4, zero_x, slot, offset, true, reg + 1));
175                 g(gen_frame_load_raw(ctx, OP_SIZE_4, zero_x, slot, offset + 4, true, reg));
176 #endif
177                 return true;
178         }
179 #endif
180 #if defined(ARCH_IA64) || defined(ARCH_PARISC)
181         if (ex == sign_x) {
182                 g(gen_address(ctx, R_FRAME, x_offset, IMM_PURPOSE_LDR_OFFSET, size));
183                 gen_insn(INSN_MOV, size, 0, 0);
184                 gen_one(reg);
185                 gen_address_offset();
187                 g(gen_extend(ctx, size, ex, reg, reg));
189                 return true;
190         }
191 #endif
192 #if defined(ARCH_POWER)
193         if (size == OP_SIZE_1 && ex == sign_x) {
194                 g(gen_address(ctx, R_FRAME, x_offset, IMM_PURPOSE_LDR_OFFSET, size));
195                 gen_insn(INSN_MOV, size, 0, 0);
196                 gen_one(reg);
197                 gen_address_offset();
199                 g(gen_extend(ctx, size, ex, reg, reg));
201                 return true;
202         }
203 #endif
204 #if defined(ARCH_S390)
205         if (size == OP_SIZE_1 && !cpu_test_feature(CPU_FEATURE_long_displacement)) {
206                 g(gen_address(ctx, R_FRAME, x_offset, IMM_PURPOSE_LDR_OFFSET, size));
207                 gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_0_8, 0);
208                 gen_one(reg);
209                 gen_one(reg);
210                 gen_address_offset();
212                 g(gen_extend(ctx, size, ex, reg, reg));
214                 return true;
215         }
216         if (size == OP_SIZE_16 && reg_is_fp(reg)) {
217                 g(gen_frame_load_raw(ctx, OP_SIZE_8, zero_x, 0, x_offset, true, reg));
218                 g(gen_frame_load_raw(ctx, OP_SIZE_8, zero_x, 0, x_offset + 8, true, reg + 2));
220                 return true;
221         }
222 #endif
223         g(gen_address(ctx, R_FRAME, x_offset, reg_is_fp(reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : ex ? IMM_PURPOSE_LDR_SX_OFFSET : IMM_PURPOSE_LDR_OFFSET, size));
224         gen_insn(unlikely(ex == sign_x) ? INSN_MOVSX : INSN_MOV, size, 0, 0);
225         gen_one(reg);
226         gen_address_offset();
228         return true;
231 static bool attr_w gen_frame_load(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, int64_t offset, bool dual, unsigned reg)
233         ajla_assert_lo((slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn)) || frame_t_is_const(slot), (file_line, "gen_frame_load: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
234         if (slot_is_register(ctx, slot)) {
235                 if (unlikely(offset != 0))
236                         internal(file_line, "gen_frame_load: offset is non-zero: %"PRIdMAX"", (intmax_t)offset);
237                 if (ex != garbage && size < OP_SIZE_NATIVE && !reg_is_fp(reg)) {
238                         g(gen_extend(ctx, size, ex, reg, ctx->registers[slot]));
239                         return true;
240                 }
241                 g(gen_mov(ctx, !reg_is_fp(reg) ? OP_SIZE_NATIVE : size, reg, ctx->registers[slot]));
242                 goto ret;
243         }
245         g(gen_frame_load_raw(ctx, size, ex, slot, offset, dual, reg));
246 ret:
247 #ifdef DEBUG_GARBAGE
248         if (size < OP_SIZE_NATIVE && ex == garbage) {
249                 uint64_t mask;
250                 g(gen_extend(ctx, size, zero_x, reg, reg));
251                 mask = (rand()) | ((uint64_t)rand() << 31) | ((uint64_t)rand() << 62);
252                 mask <<= 8ULL << size;
253                 g(gen_imm(ctx, mask, IMM_PURPOSE_OR, OP_SIZE_NATIVE));
254                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, ALU_WRITES_FLAGS(OP_SIZE_NATIVE, ALU_OR, false, is_imm(), ctx->const_imm));
255                 gen_one(reg);
256                 gen_one(reg);
257                 gen_imm_offset();
258         }
259 #endif
260         return true;
263 static bool attr_w gen_frame_get(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, unsigned reg, unsigned *dest)
265         const struct type *t;
266         if (frame_t_is_const(slot)) {
267                 g(gen_frame_load(ctx, size, ex, slot, 0, false, reg));
268                 *dest = reg;
269                 return true;
270         }
271         t = get_type_of_local(ctx, slot);
272         ajla_assert_lo(slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn), (file_line, "gen_frame_get: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
273         if (slot_is_register(ctx, slot)) {
274                 unsigned reg = ctx->registers[slot];
275                 if (ex != garbage && size < OP_SIZE_NATIVE && !reg_is_fp(reg)) {
276                         if (t->tag == TYPE_TAG_flat_option && size <= ARCH_BOOL_SIZE)
277                                 goto skip_extend;
278                         g(gen_extend(ctx, size, ex, reg, reg));
279                 }
280 skip_extend:
281                 *dest = reg;
282                 goto ret;
283         }
284         *dest = reg;
285         g(gen_frame_load(ctx, size, ex, slot, 0, false, reg));
286 ret:
287 #ifdef DEBUG_GARBAGE
288         if (size < OP_SIZE_NATIVE && ex == garbage && t->tag != TYPE_TAG_flat_option) {
289                 uint64_t mask;
290                 g(gen_extend(ctx, size, zero_x, *dest, *dest));
291                 mask = (rand()) | ((uint64_t)rand() << 31) | ((uint64_t)rand() << 62);
292                 mask <<= 8ULL << size;
293                 g(gen_imm(ctx, mask, IMM_PURPOSE_OR, OP_SIZE_NATIVE));
294                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, ALU_WRITES_FLAGS(OP_SIZE_NATIVE, ALU_OR, false, is_imm(), ctx->const_imm));
295                 gen_one(*dest);
296                 gen_one(*dest);
297                 gen_imm_offset();
298         }
299 #endif
300         return true;
303 #if defined(ARCH_X86)
304 static bool attr_w gen_frame_load_x87(struct codegen_context *ctx, unsigned insn, unsigned size, unsigned alu, frame_t slot)
306         if (slot_is_register(ctx, slot))
307                 g(spill(ctx,slot));
308         g(gen_address(ctx, R_FRAME, (size_t)slot * slot_size, IMM_PURPOSE_LDR_OFFSET, size));
309         gen_insn(insn, size, alu, 0);
310         gen_address_offset();
311         return true;
314 static bool attr_w gen_frame_store_x87(struct codegen_context *ctx, unsigned insn, unsigned size, frame_t slot)
316         g(gen_address(ctx, R_FRAME, (size_t)slot * slot_size, IMM_PURPOSE_STR_OFFSET, size));
317         gen_insn(insn, size, 0, 0);
318         gen_address_offset();
319         if (slot_is_register(ctx, slot))
320                 g(unspill(ctx,slot));
321         return true;
323 #endif
325 static bool attr_w gen_frame_load_op(struct codegen_context *ctx, unsigned size, enum extend ex, unsigned alu, unsigned writes_flags, frame_t slot, int64_t offset, bool dual, unsigned reg)
327         ajla_assert_lo((slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn)) || frame_t_is_const(slot), (file_line, "gen_frame_load_op: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
328         if (slot_is_register(ctx, slot)) {
329                 if (size != i_size(size) + (unsigned)zero && ex != garbage)
330                         goto fallback;
331                 g(gen_3address_alu(ctx, i_size(size), alu, reg, reg, ctx->registers[slot], writes_flags));
332                 return true;
333         }
334         if (frame_t_is_const(slot)) {
335                 g(gen_imm(ctx, get_constant(size, ex, slot, offset, dual), alu_purpose(alu), i_size(size)));
336                 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), i_size(size), alu, ALU_WRITES_FLAGS(i_size(size), alu, false, is_imm(), ctx->const_imm) | writes_flags);
337                 gen_one(reg);
338                 gen_one(reg);
339                 gen_imm_offset();
340                 return true;
341         }
342 #if defined(ARCH_X86) || defined(ARCH_S390)
343 #if defined(ARCH_S390)
344         if (size >= OP_SIZE_4)
345 #endif
346         {
347                 offset += (size_t)slot * slot_size;
348                 g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDR_OFFSET, size));
349                 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(size, alu, true, false, 0) | writes_flags);
350                 gen_one(reg);
351                 gen_one(reg);
352                 gen_address_offset();
353                 return true;
354         }
355 #endif
356 fallback:
357 #if defined(R_SCRATCH_NA_1)
358         g(gen_frame_load(ctx, size, ex, slot, offset, dual, R_SCRATCH_NA_1));
359         g(gen_3address_alu(ctx, i_size(size), alu, reg, reg, R_SCRATCH_NA_1, writes_flags));
360 #endif
361         return true;
364 static bool attr_w attr_unused gen_frame_load_op1(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned writes_flags, frame_t slot, unsigned reg)
366         ajla_assert_lo(slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn), (file_line, "gen_frame_load_op1: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
367         if (slot_is_register(ctx, slot)) {
368                 g(gen_2address_alu1(ctx, size, alu, reg, ctx->registers[slot], writes_flags));
369                 return true;
370         }
371 #if defined(ARCH_X86)
372         {
373                 int64_t offset = (size_t)slot * slot_size;
374                 g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDR_OFFSET, size));
375                 gen_insn(INSN_ALU1 + ARCH_PARTIAL_ALU(size), size, alu, ALU1_WRITES_FLAGS(alu) | writes_flags);
376                 gen_one(reg);
377                 gen_address_offset();
378                 return true;
379         }
380 #endif
381 #if !defined(ARCH_X86)
382         g(gen_frame_load(ctx, size, garbage, slot, 0, false, reg));
383         g(gen_2address_alu1(ctx, size, alu, reg, reg, writes_flags));
384         return true;
385 #endif
388 #if ARCH_HAS_FLAGS
389 static bool attr_w gen_frame_load_cmp(struct codegen_context *ctx, unsigned size, bool logical, enum extend attr_unused ex, bool swap, frame_t slot, int64_t offset, bool dual, unsigned reg)
391         if (slot_is_register(ctx, slot)) {
392                 if (size != i_size_cmp(size) + (unsigned)zero && ex != garbage)
393                         goto fallback;
394                 gen_insn(INSN_CMP, i_size_cmp(size), 0, 1 + logical);
395                 if (!swap) {
396                         gen_one(reg);
397                         gen_one(ctx->registers[slot]);
398                 } else {
399                         gen_one(ctx->registers[slot]);
400                         gen_one(reg);
401                 }
402                 return true;
403         }
404         if (frame_t_is_const(slot)) {
405                 if (!swap) {
406                         g(gen_imm(ctx, get_constant(size, ex, slot, offset, dual), !logical ? IMM_PURPOSE_CMP : IMM_PURPOSE_CMP_LOGICAL, size));
407                         gen_insn(INSN_CMP, i_size_cmp(size), 0, 1 + logical);
408                         gen_one(reg);
409                         gen_imm_offset();
410                 } else {
411                         g(gen_load_constant(ctx, R_CONST_IMM, get_constant(size, ex, slot, offset, dual)));
412                         gen_insn(INSN_CMP, i_size_cmp(size), 0, 1 + logical);
413                         gen_one(R_CONST_IMM);
414                         gen_one(reg);
415                 }
416                 return true;
417         }
418 #if defined(ARCH_S390) || defined(ARCH_X86)
419 #if defined(ARCH_S390)
420         if (size < OP_SIZE_4)
421                 goto fallback;
422 #endif
423         offset += (size_t)slot * slot_size;
424         g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDR_OFFSET, size));
425         gen_insn(INSN_CMP, size, 0, 1 + logical);
426         if (!swap) {
427                 gen_one(reg);
428                 gen_address_offset();
429         } else {
430                 gen_address_offset();
431                 gen_one(reg);
432         }
433         return true;
434 #endif
435 fallback:
436 #if defined(R_SCRATCH_NA_1)
437         g(gen_frame_load(ctx, size, ex, slot, offset, false, R_SCRATCH_NA_1));
438         gen_insn(INSN_CMP, i_size_cmp(size), 0, 1 + logical);
439         if (!swap) {
440                 gen_one(reg);
441                 gen_one(R_SCRATCH_NA_1);
442         } else {
443                 gen_one(R_SCRATCH_NA_1);
444                 gen_one(reg);
445         }
446 #endif
447         return true;
450 static bool attr_w gen_frame_load_cmp_imm(struct codegen_context *ctx, unsigned size, bool logical, enum extend attr_unused ex, frame_t slot, int64_t offset, int64_t value)
452         if (slot_is_register(ctx, slot)) {
453 #if defined(ARCH_X86)
454                 g(gen_imm(ctx, value, logical ? IMM_PURPOSE_CMP_LOGICAL : IMM_PURPOSE_CMP, size));
455                 gen_insn(INSN_CMP, size, 0, 1 + logical);
456                 gen_one(ctx->registers[slot]);
457                 gen_imm_offset();
458 #else
459                 if (size != i_size(size) + (unsigned)zero && size < OP_SIZE_4 && ex != garbage)
460                         goto fallback;
461                 g(gen_imm(ctx, value, logical ? IMM_PURPOSE_CMP_LOGICAL : IMM_PURPOSE_CMP, size));
462                 gen_insn(INSN_CMP, i_size_cmp(size), 0, 1 + logical);
463                 gen_one(ctx->registers[slot]);
464                 gen_imm_offset();
465 #endif
466                 return true;
467         }
468 #if defined(ARCH_X86)
469         offset += (size_t)slot * slot_size;
470         g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_MVI_CLI_OFFSET, size));
471         g(gen_imm(ctx, value, logical ? IMM_PURPOSE_CMP_LOGICAL : IMM_PURPOSE_CMP, size));
472         gen_insn(INSN_CMP, size, 0, 1 + logical);
473         gen_address_offset();
474         gen_imm_offset();
475         return true;
476 #endif
477 #if defined(ARCH_S390)
478         if (size != OP_SIZE_1 || !logical)
479                 goto fallback;
480         offset += (size_t)slot * slot_size;
481         g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_MVI_CLI_OFFSET, size));
482         gen_insn(INSN_CMP, size, 0, 1 + logical);
483         gen_address_offset();
484         gen_one(ARG_IMM);
485         gen_eight((int8_t)value);
486         return true;
487 #endif
488 #if defined(R_SCRATCH_NA_1)
489         goto fallback;
490 fallback:
491         g(gen_frame_load(ctx, size, ex, slot, offset, false, R_SCRATCH_NA_1));
492         g(gen_imm(ctx, value, logical ? IMM_PURPOSE_CMP_LOGICAL : IMM_PURPOSE_CMP, size));
493         gen_insn(INSN_CMP, i_size(size), 0, 1 + logical);
494         gen_one(R_SCRATCH_NA_1);
495         gen_imm_offset();
496         return true;
497 #endif
499 #endif
501 static bool attr_w gen_frame_load_2(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, unsigned reg1, unsigned reg2)
503         if (frame_t_is_const(slot))
504                 goto skip_ldd;
505 #if defined(ARCH_ARM64)
506         offset += (size_t)slot * slot_size;
507         g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDP_STP_OFFSET, size));
508         gen_insn(INSN_LDP, size, 0, 0);
509         gen_one(reg1);
510         gen_one(reg2);
511         gen_address_offset();
512         return true;
513 #endif
514 #if defined(ARCH_ARM32)
515         if (likely(!(reg1 & 1)) && likely(reg2 == reg1 + 1) && likely(cpu_test_feature(CPU_FEATURE_armv6)))
516 #elif defined(ARCH_SPARC32)
517         if (likely(!(reg2 & 1)) && likely(reg1 == reg2 + 1))
518 #elif defined(ARCH_S390)
519         if (likely(reg1 == reg2 + 1))
520 #else
521         if (0)
522 #endif
523         {
524                 offset += (size_t)slot * slot_size;
525                 if (UNALIGNED_TRAP) {
526                         if (unlikely((offset & ((2U << size) - 1)) != 0)) {
527                                 offset -= (size_t)slot * slot_size;
528                                 goto skip_ldd;
529                         }
530                 }
531                 g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDP_STP_OFFSET, size));
532                 gen_insn(INSN_LDP, size, 0, 0);
533                 gen_one(reg1);
534                 gen_one(reg2);
535                 gen_address_offset();
536                 return true;
537         }
538         goto skip_ldd;
539 skip_ldd:
540         g(gen_frame_load(ctx, size, garbage, slot, offset + lo_word(size), true, reg1));
541         g(gen_frame_load(ctx, size, garbage, slot, offset + hi_word(size), true, reg2));
542         return true;
545 static bool attr_w gen_frame_store_raw(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, unsigned reg)
547         offset += (size_t)slot * slot_size;
548         if (!ARCH_HAS_BWX)
549                 size = maximum(OP_SIZE_4, size);
550 #if defined(ARCH_MIPS)
551         if (reg_is_fp(reg) && size == OP_SIZE_8 && !MIPS_HAS_LS_DOUBLE) {
552 #if defined(C_LITTLE_ENDIAN)
553                 g(gen_frame_store_raw(ctx, OP_SIZE_4, 0, offset, reg));
554                 g(gen_frame_store_raw(ctx, OP_SIZE_4, 0, offset + 4, reg + 1));
555 #else
556                 g(gen_frame_store_raw(ctx, OP_SIZE_4, 0, offset, reg + 1));
557                 g(gen_frame_store_raw(ctx, OP_SIZE_4, 0, offset + 4, reg));
558 #endif
559                 return true;
560         }
561 #endif
562 #if defined(ARCH_S390)
563         if (size == OP_SIZE_16 && reg_is_fp(reg)) {
564                 g(gen_frame_store_raw(ctx, OP_SIZE_8, 0, offset, reg));
565                 g(gen_frame_store_raw(ctx, OP_SIZE_8, 0, offset + 8, reg + 2));
566                 return true;
567         }
568 #endif
569         g(gen_address(ctx, R_FRAME, offset, reg_is_fp(reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : IMM_PURPOSE_STR_OFFSET, size));
570         gen_insn(INSN_MOV, size, 0, 0);
571         gen_address_offset();
572         gen_one(reg);
573         return true;
576 static bool attr_w gen_frame_store(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, unsigned reg)
578         ajla_assert_lo(slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn), (file_line, "gen_frame_store: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
579         if (slot_is_register(ctx, slot)) {
580                 if (unlikely(offset != 0))
581                         internal(file_line, "gen_frame_store: offset is non-zero: %"PRIdMAX"", (intmax_t)offset);
582                 g(gen_mov(ctx, !reg_is_fp(reg) ? OP_SIZE_NATIVE : size, ctx->registers[slot], reg));
583                 return true;
584         }
585         return gen_frame_store_raw(ctx, size, slot, offset, reg);
588 static unsigned gen_frame_target(struct codegen_context *ctx, frame_t slot_r, frame_t slot_na_1, frame_t slot_na_2, unsigned reg)
590         if (slot_is_register(ctx, slot_r)) {
591                 short d = ctx->registers[slot_r];
592                 if (slot_na_1 != NO_FRAME_T && slot_is_register(ctx, slot_na_1) && ctx->registers[slot_na_1] == d)
593                         return reg;
594                 if (slot_na_2 != NO_FRAME_T && slot_is_register(ctx, slot_na_2) && ctx->registers[slot_na_2] == d)
595                         return reg;
596                 return d;
597         }
598         return reg;
601 static bool attr_w gen_frame_store_2(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, unsigned reg1, unsigned reg2)
603 #if defined(ARCH_ARM64)
604         offset += (size_t)slot * slot_size;
605         g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDP_STP_OFFSET, size));
606         gen_insn(INSN_STP, size, 0, 0);
607         gen_address_offset();
608         gen_one(reg1);
609         gen_one(reg2);
610         return true;
611 #endif
612 #if defined(ARCH_ARM32)
613         if (likely(!(reg1 & 1)) && likely(reg2 == reg1 + 1) && likely(cpu_test_feature(CPU_FEATURE_armv6)))
614 #elif defined(ARCH_SPARC32)
615         if (likely(!(reg2 & 1)) && likely(reg1 == reg2 + 1))
616 #elif defined(ARCH_S390)
617         if (likely(reg1 == reg2 + 1))
618 #else
619         if (0)
620 #endif
621         {
622                 offset += (size_t)slot * slot_size;
623                 if (UNALIGNED_TRAP) {
624                         if (unlikely((offset & ((2U << size) - 1)) != 0)) {
625                                 offset -= (size_t)slot * slot_size;
626                                 goto skip_ldd;
627                         }
628                 }
629                 g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_LDP_STP_OFFSET, size));
630                 gen_insn(INSN_STP, size, 0, 0);
631                 gen_address_offset();
632                 gen_one(reg1);
633                 gen_one(reg2);
634                 return true;
635         }
636         goto skip_ldd;
637 skip_ldd:
638         g(gen_frame_store(ctx, size, slot, offset + lo_word(size), reg1));
639         g(gen_frame_store(ctx, size, slot, offset + hi_word(size), reg2));
640         return true;
643 static bool attr_w gen_frame_store_imm_raw(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, int64_t imm)
645         offset += (size_t)slot * slot_size;
646         if (!ARCH_HAS_BWX)
647                 size = maximum(OP_SIZE_4, size);
648         g(gen_address(ctx, R_FRAME, offset, size == OP_SIZE_1 ? IMM_PURPOSE_MVI_CLI_OFFSET : IMM_PURPOSE_STR_OFFSET, size));
649         g(gen_imm(ctx, imm, IMM_PURPOSE_STORE_VALUE, size));
650         gen_insn(INSN_MOV, size, 0, 0);
651         gen_address_offset();
652         gen_imm_offset();
653         return true;
656 static bool attr_w gen_frame_store_imm(struct codegen_context *ctx, unsigned size, frame_t slot, int64_t offset, int64_t imm)
658         ajla_assert_lo(slot >= MIN_USEABLE_SLOT && slot < function_n_variables(ctx->fn), (file_line, "gen_frame_store_imm: invalid slot: %lu >= %lu", (unsigned long)slot, (unsigned long)function_n_variables(ctx->fn)));
659         if (slot_is_register(ctx, slot)) {
660                 if (unlikely(offset != 0))
661                         internal(file_line, "gen_frame_store_imm: offset is non-zero: %"PRIdMAX"", (intmax_t)offset);
662                 if (size == OP_SIZE_1)
663                         imm = ARCH_PREFERS_SX(size) ? (int64_t)(int8_t)imm : (int64_t)(uint8_t)imm;
664                 if (size == OP_SIZE_2)
665                         imm = ARCH_PREFERS_SX(size) ? (int64_t)(int16_t)imm : (int64_t)(uint16_t)imm;
666                 if (size == OP_SIZE_4)
667                         imm = ARCH_PREFERS_SX(size) ? (int64_t)(int32_t)imm : (int64_t)(uint32_t)imm;
668                 g(gen_load_constant(ctx, ctx->registers[slot], imm));
669                 return true;
670         }
671         return gen_frame_store_imm_raw(ctx, size, slot, offset, imm);
674 static bool attr_w gen_frame_clear_raw(struct codegen_context *ctx, unsigned size, frame_t slot)
676         g(gen_frame_store_imm_raw(ctx, size, slot, 0, 0));
677         return true;
680 static bool attr_w gen_frame_clear(struct codegen_context *ctx, unsigned size, frame_t slot)
682         g(gen_frame_store_imm(ctx, size, slot, 0, 0));
683         if (slot_is_register(ctx, slot))
684                 g(spill(ctx, slot));
685         return true;
688 #if defined(ARCH_X86)
689 static bool attr_w gen_frame_set_cond(struct codegen_context *ctx, unsigned attr_unused size, bool attr_unused logical, unsigned cond, frame_t slot)
691         size_t offset;
692         if (slot_is_register(ctx, slot)) {
693                 unsigned reg = ctx->registers[slot];
694 #if defined(ARCH_X86_32)
695                 if (reg >= 4) {
696                         gen_insn(INSN_SET_COND_PARTIAL, OP_SIZE_1, cond, 0);
697                         gen_one(R_SCRATCH_1);
698                         gen_one(R_SCRATCH_1);
700                         g(gen_mov(ctx, OP_SIZE_1, reg, R_SCRATCH_1));
701                         return true;
702                 }
703 #endif
704                 if (cpu_test_feature(CPU_FEATURE_apx)) {
705                         gen_insn(INSN_SET_COND, OP_SIZE_8, cond, 0);
706                         gen_one(reg);
707                 } else {
708                         gen_insn(INSN_SET_COND_PARTIAL, OP_SIZE_1, cond, 0);
709                         gen_one(reg);
710                         gen_one(reg);
711                         if (sizeof(ajla_flat_option_t) > 1)
712                                 g(gen_mov(ctx, OP_SIZE_1, reg, reg));
713                 }
714                 return true;
715         }
716         offset = (size_t)slot * slot_size;
717         if (sizeof(ajla_flat_option_t) > 1) {
718                 gen_insn(INSN_SET_COND_PARTIAL, OP_SIZE_1, cond, 0);
719                 gen_one(R_SCRATCH_1);
720                 gen_one(R_SCRATCH_1);
722                 g(gen_mov(ctx, OP_SIZE_1, R_SCRATCH_1, R_SCRATCH_1));
724                 g(gen_frame_store(ctx, log_2(sizeof(ajla_flat_option_t)), slot, 0, R_SCRATCH_1));
725         } else {
726                 g(gen_address(ctx, R_FRAME, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_1));
727                 gen_insn(INSN_SET_COND, OP_SIZE_1, cond, 0);
728                 gen_address_offset();
729         }
730         return true;
732 #elif defined(ARCH_ARM64)
733 static bool attr_w gen_frame_set_cond(struct codegen_context *ctx, unsigned attr_unused size, bool attr_unused logical, unsigned cond, frame_t slot)
735         if (slot_is_register(ctx, slot)) {
736                 gen_insn(INSN_SET_COND, OP_SIZE_4, cond, 0);
737                 gen_one(ctx->registers[slot]);
738         } else {
739                 gen_insn(INSN_SET_COND, OP_SIZE_4, cond, 0);
740                 gen_one(R_SCRATCH_1);
741                 g(gen_frame_store(ctx, log_2(sizeof(ajla_flat_option_t)), slot, 0, R_SCRATCH_1));
742         }
743         return true;
745 #elif ARCH_HAS_FLAGS
746 static bool attr_w gen_frame_set_cond(struct codegen_context *ctx, unsigned size, bool logical, unsigned cond, frame_t slot)
748         unsigned target = gen_frame_target(ctx, slot, NO_FRAME_T, NO_FRAME_T, R_SCRATCH_1);
749 #if defined(ARCH_POWER)
750         if (!cpu_test_feature(CPU_FEATURE_v203))
751 #elif defined(ARCH_S390)
752         if (!cpu_test_feature(CPU_FEATURE_misc_45))
753 #elif defined(ARCH_SPARC32)
754         if (!SPARC_9)
755 #else
756         if (0)
757 #endif
758         {
759                 uint32_t label;
760                 g(gen_load_constant(ctx, target, 1));
761                 label = alloc_label(ctx);
762                 if (unlikely(!label))
763                         return false;
764                 gen_insn(!logical ? INSN_JMP_COND : INSN_JMP_COND_LOGICAL, i_size_cmp(size), cond, 0);
765                 gen_four(label);
766                 g(gen_load_constant(ctx, target, 0));
767                 gen_label(label);
768                 goto do_store;
769         }
770         g(gen_load_constant(ctx, target, 1));
771         g(gen_imm(ctx, 0, IMM_PURPOSE_CMOV, OP_SIZE_NATIVE));
772         if (cond & COND_FP) {
773                 gen_insn(INSN_CMOV, OP_SIZE_NATIVE, cond ^ 1, 0);
774         } else {
775 #if defined(ARCH_S390)
776                 gen_insn(logical ? INSN_CMOV_XCC : INSN_CMOV, OP_SIZE_NATIVE, cond ^ 1, 0);
777 #else
778                 gen_insn(size == OP_SIZE_8 ? INSN_CMOV_XCC : INSN_CMOV, OP_SIZE_NATIVE, cond ^ 1, 0);
779 #endif
780         }
781         gen_one(target);
782         gen_one(target);
783         gen_imm_offset();
784 do_store:
785         g(gen_frame_store(ctx, log_2(sizeof(ajla_flat_option_t)), slot, 0, target));
786         return true;
788 #endif
790 #if !ARCH_HAS_FLAGS
791 static bool attr_w gen_frame_cmp_imm_set_cond_reg(struct codegen_context *ctx, unsigned size, unsigned reg, int64_t imm, unsigned cond, frame_t slot_r)
793         unsigned dest_reg;
794         dest_reg = gen_frame_target(ctx, slot_r, NO_FRAME_T, NO_FRAME_T, R_CMP_RESULT);
795         g(gen_cmp_dest_reg(ctx, size, reg, (unsigned)-1, dest_reg, imm, cond));
796         g(gen_frame_store(ctx, log_2(sizeof(ajla_flat_option_t)), slot_r, 0, dest_reg));
798         return true;
800 #endif
802 static bool attr_w gen_frame_load_cmp_set_cond(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, unsigned reg, unsigned cond, frame_t slot_r)
804 #if ARCH_HAS_FLAGS
805         bool logical = COND_IS_LOGICAL(cond);
806         g(gen_frame_load_cmp(ctx, size, logical, ex, false, slot, 0, false, reg));
807         g(gen_frame_set_cond(ctx, size, logical, cond, slot_r));
808 #else
809         unsigned src_reg, dest_reg;
810         g(gen_frame_get(ctx, size, ex, slot, R_SCRATCH_NA_1, &src_reg));
811         dest_reg = gen_frame_target(ctx, slot_r, NO_FRAME_T, NO_FRAME_T, R_SCRATCH_NA_1);
812         g(gen_cmp_dest_reg(ctx, size, reg, src_reg, dest_reg, 0, cond));
813         g(gen_frame_store(ctx, log_2(sizeof(ajla_flat_option_t)), slot_r, 0, dest_reg));
814 #endif
815         return true;
818 static bool attr_w gen_frame_load_cmp_imm_set_cond(struct codegen_context *ctx, unsigned size, enum extend ex, frame_t slot, int64_t value, unsigned cond, frame_t slot_r)
820 #if ARCH_HAS_FLAGS
821         bool logical = COND_IS_LOGICAL(cond);
822 #if defined(ARCH_S390)
823         if (cond == COND_E)
824                 logical = true;
825 #endif
826         g(gen_frame_load_cmp_imm(ctx, size, logical, ex, slot, 0, value));
827         g(gen_frame_set_cond(ctx, size, false, cond, slot_r));
828 #else
829         unsigned src_reg;
830         g(gen_frame_get(ctx, size, ex, slot, R_SCRATCH_NA_1, &src_reg));
831         g(gen_frame_cmp_imm_set_cond_reg(ctx, size, src_reg, value, cond, slot_r));
832 #endif
833         return true;
836 static bool attr_w gen_upcall_start(struct codegen_context *ctx, unsigned offset, unsigned args)
838         unsigned idx;
839         size_t i;
840         int attr_unused reg_to_push = -1;
841         ajla_assert_lo(ctx->upcall_args == -1, (file_line, "gen_upcall_start: gen_upcall_end not called"));
842         ajla_assert_lo(ctx->upcall_offset == -1, (file_line, "gen_upcall_start: gen_upcall_end not called"));
843         ctx->upcall_args = (int)args;
844         ctx->upcall_offset = (int)offset;
845         idx = offset / sizeof(void *);
846         ctx->upcall_hacked_abi = false;
847         if (idx < 32 && (hacked_upcall_map >> idx) & 1)
848                 ctx->upcall_hacked_abi = true;
850 #if (defined(ARCH_X86_64) || defined(ARCH_X86_X32)) && !defined(ARCH_X86_WIN_ABI)
851         for (i = 0; i < ctx->need_spill_l; i++) {
852                 unsigned reg = ctx->registers[ctx->need_spill[i]];
853                 if (reg_is_fp(reg))
854                         g(spill(ctx, ctx->need_spill[i]));
855         }
856         ctx->n_pushes = 0;
857         for (i = 0; i < ctx->need_spill_l; i++) {
858                 unsigned reg = ctx->registers[ctx->need_spill[i]];
859                 if (!reg_is_fp(reg)) {
860                         if (ctx->upcall_hacked_abi && !reg_is_saved(reg))
861                                 continue;
862                         if (reg_to_push < 0) {
863                                 reg_to_push = reg;
864                         } else {
865                                 g(gen_x86_push2(ctx, reg_to_push, reg, true));
866                                 reg_to_push = -1;
867                         }
868                         ctx->n_pushes++;
869                 }
870         }
871         if (ctx->n_pushes & 1) {
872                 g(gen_x86_push2(ctx, reg_to_push, R_CX, true));
873         }
874 #else
875         for (i = 0; i < ctx->need_spill_l; i++)
876                 g(spill(ctx, ctx->need_spill[i]));
877 #endif
878         return true;
881 static bool attr_w gen_upcall_end(struct codegen_context *ctx, unsigned attr_unused offset, unsigned attr_unused args)
883         size_t i;
884         int attr_unused reg_to_pop = -1;
885         ajla_assert_lo(ctx->upcall_args == (int)args, (file_line, "gen_upcall_end: gen_upcall_start args mismatch: %d != %d", ctx->upcall_args, (int)args));
886         ajla_assert_lo(ctx->upcall_offset == (int)offset, (file_line, "gen_upcall_end: gen_upcall_start offset mismatch: %d != %d", ctx->upcall_offset, (int)offset));
888 #if (defined(ARCH_X86_64) || defined(ARCH_X86_X32)) && !defined(ARCH_X86_WIN_ABI)
889         if (ctx->n_pushes & 1) {
890                 reg_to_pop = R_CX;
891         }
892         for (i = ctx->need_spill_l; i;) {
893                 unsigned reg;
894                 i--;
895                 reg = ctx->registers[ctx->need_spill[i]];
896                 if (!reg_is_fp(reg)) {
897                         if (ctx->upcall_hacked_abi && !reg_is_saved(reg))
898                                 continue;
899                         if (reg_to_pop < 0) {
900                                 reg_to_pop = reg;
901                         } else {
902                                 g(gen_x86_pop2(ctx, reg_to_pop, reg, true));
903                                 reg_to_pop = -1;
904                         }
905                 }
906         }
907         ajla_assert_lo(reg_to_pop == -1, (file_line, "gen_upcall_end: reg_to_pop mismatch"));
908         for (i = 0; i < ctx->need_spill_l; i++) {
909                 unsigned reg = ctx->registers[ctx->need_spill[i]];
910                 if (reg_is_fp(reg))
911                         g(unspill(ctx, ctx->need_spill[i]));
912         }
913 #else
914         for (i = 0; i < ctx->need_spill_l; i++)
915                 g(unspill(ctx, ctx->need_spill[i]));
916 #endif
918         ctx->upcall_args = -1;
919         ctx->upcall_offset = -1;
920         ctx->upcall_hacked_abi = false;
921         return true;
924 static bool attr_w gen_memcpy_raw(struct codegen_context *ctx, unsigned dest_base, int64_t dest_offset, unsigned src_base, int64_t src_offset, size_t size, size_t attr_unused align)
926         if (!size)
927                 return true;
928         if (!ARCH_HAS_BWX) {
929                 if (align < 4 || (size & 3))
930                         goto call_memcpy;
931         }
932 #if defined(ARCH_S390)
933         if (size <= 0x10) {
934                 if (!(size & 3) || (cpu_test_feature(CPU_FEATURE_long_displacement) && cpu_test_feature(CPU_FEATURE_extended_imm)))
935                         goto do_explicit_copy;
936         }
937         if (size <= 0x100 && dest_offset >= 0 && dest_offset < 0x1000 && src_offset >= 0 && src_offset < 0x1000) {
938                 gen_insn(INSN_MEMCPY, 0, 0, 0);
939                 gen_one(ARG_ADDRESS_1);
940                 gen_one(dest_base);
941                 gen_eight(dest_offset);
942                 gen_one(ARG_ADDRESS_1);
943                 gen_one(src_base);
944                 gen_eight(src_offset);
945                 gen_one(ARG_IMM);
946                 gen_eight(size);
947                 return true;
948         }
949         goto call_memcpy;
950 do_explicit_copy:
951 #endif
952         if (size <= INLINE_COPY_SIZE) {
953                 while (size) {
954                         unsigned this_step;
955                         unsigned this_op_size;
956 #if defined(ARCH_ARM)
957                         if (size >= 2U << OP_SIZE_NATIVE
958 #if defined(ARCH_ARM32)
959                                 && align >= 1U << OP_SIZE_NATIVE
960 #endif
961                         ) {
962                                 g(gen_address(ctx, src_base, src_offset, IMM_PURPOSE_LDP_STP_OFFSET, OP_SIZE_NATIVE));
963                                 gen_insn(INSN_LDP, OP_SIZE_NATIVE, 0, 0);
964                                 gen_one(R_SCRATCH_NA_1);
965                                 gen_one(R_SCRATCH_NA_2);
966                                 gen_address_offset();
968                                 g(gen_address(ctx, dest_base, dest_offset, IMM_PURPOSE_LDP_STP_OFFSET, OP_SIZE_NATIVE));
969                                 gen_insn(INSN_STP, OP_SIZE_NATIVE, 0, 0);
970                                 gen_address_offset();
971                                 gen_one(R_SCRATCH_NA_1);
972                                 gen_one(R_SCRATCH_NA_2);
974                                 size -= 2U << OP_SIZE_NATIVE;
975                                 src_offset += 2U << OP_SIZE_NATIVE;
976                                 dest_offset += 2U << OP_SIZE_NATIVE;
978                                 continue;
979                         }
980 #endif
981                         if (size >= 8 && OP_SIZE_NATIVE >= OP_SIZE_8)
982                                 this_step = 8;
983                         else if (size >= 4)
984                                 this_step = 4;
985                         else if (size >= 2)
986                                 this_step = 2;
987                         else
988                                 this_step = 1;
989                         if (UNALIGNED_TRAP)
990                                 this_step = minimum(this_step, align);
991                         this_op_size = log_2(this_step);
993                         g(gen_address(ctx, src_base, src_offset, ARCH_PREFERS_SX(this_op_size) ? IMM_PURPOSE_LDR_SX_OFFSET : IMM_PURPOSE_LDR_OFFSET, this_op_size));
994                         gen_insn(ARCH_PREFERS_SX(this_op_size) ? INSN_MOVSX : INSN_MOV, this_op_size, 0, 0);
995                         gen_one(R_SCRATCH_1);
996                         gen_address_offset();
998                         g(gen_address(ctx, dest_base, dest_offset, IMM_PURPOSE_STR_OFFSET, this_op_size));
999                         gen_insn(INSN_MOV, this_op_size, 0, 0);
1000                         gen_address_offset();
1001                         gen_one(R_SCRATCH_1);
1003                         size -= this_step;
1004                         src_offset += this_step;
1005                         dest_offset += this_step;
1006                 }
1007                 return true;
1008         }
1010 call_memcpy:
1011         g(gen_upcall_start(ctx, offsetof(struct cg_upcall_vector_s, mem_copy), 3));
1012         if (unlikely(R_ARG0 == src_base)) {
1013                 if (unlikely(R_ARG1 == dest_base))
1014                         internal(file_line, "gen_memcpy_raw: swapped registers: %u, %u", src_base, dest_base);
1015                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, R_ARG1, src_base, src_offset, 0));
1016                 g(gen_upcall_argument(ctx, 1));
1017         }
1019         g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, R_ARG0, dest_base, dest_offset, 0));
1020         g(gen_upcall_argument(ctx, 0));
1022         if (R_ARG0 != src_base) {
1023                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, R_ARG1, src_base, src_offset, 0));
1024                 g(gen_upcall_argument(ctx, 1));
1025         }
1027 #if (defined(ARCH_X86_64) || defined(ARCH_X86_X32)) && !defined(ARCH_X86_WIN_ABI)
1028         if (cpu_test_feature(CPU_FEATURE_erms)) {
1029                 g(gen_load_constant(ctx, R_CX, size));
1031                 gen_insn(INSN_MEMCPY, 0, 0, 0);
1032                 gen_one(ARG_ADDRESS_1_POST_I);
1033                 gen_one(R_DI);
1034                 gen_eight(0);
1035                 gen_one(ARG_ADDRESS_1_POST_I);
1036                 gen_one(R_SI);
1037                 gen_eight(0);
1038                 gen_one(R_CX);
1039                 g(gen_upcall_end(ctx, offsetof(struct cg_upcall_vector_s, mem_copy), 3));
1040                 return true;
1041         }
1042 #endif
1044         g(gen_load_constant(ctx, R_ARG2, size));
1045         g(gen_upcall_argument(ctx, 2));
1047         g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, mem_copy), 3));
1049         return true;
1052 static bool attr_w gen_memcpy_to_slot(struct codegen_context *ctx, frame_t dest_slot, unsigned src_base, int64_t src_offset)
1054         const struct type *t = get_type_of_local(ctx, dest_slot);
1055         unsigned size = spill_size(t);
1056         short dest_reg = ctx->registers[dest_slot];
1057         if (dest_reg >= 0) {
1058                 if (ARCH_PREFERS_SX(size) && !reg_is_fp(dest_reg)) {
1059 #if defined(ARCH_S390)
1060                         if (size == OP_SIZE_1 && !cpu_test_feature(CPU_FEATURE_long_displacement)) {
1061                                 g(gen_address(ctx, src_base, src_offset, IMM_PURPOSE_LDR_OFFSET, size));
1062                                 gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_0_8, 0);
1063                                 gen_one(dest_reg);
1064                                 gen_one(dest_reg);
1065                                 gen_address_offset();
1066                                 g(gen_extend(ctx, size, sign_x, dest_reg, dest_reg));
1067                                 return true;
1068                         }
1069 #endif
1070                         g(gen_address(ctx, src_base, src_offset, IMM_PURPOSE_LDR_SX_OFFSET, size));
1071                         gen_insn(INSN_MOVSX, size, 0, 0);
1072                 } else {
1073                         g(gen_address(ctx, src_base, src_offset, reg_is_fp(dest_reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : IMM_PURPOSE_LDR_OFFSET, size));
1074                         gen_insn(INSN_MOV, size, 0, 0);
1075                 }
1076                 gen_one(dest_reg);
1077                 gen_address_offset();
1078                 return true;
1079         }
1080         g(gen_memcpy_raw(ctx, R_FRAME, (size_t)dest_slot * slot_size, src_base, src_offset, t->size, t->align));
1081         return true;
1084 static bool attr_w gen_memcpy_from_slot(struct codegen_context *ctx, unsigned dest_base, int64_t dest_offset, frame_t src_slot)
1086         const struct type *t = get_type_of_local(ctx, src_slot);
1087         short src_reg = ctx->registers[src_slot];
1088         if (src_reg >= 0) {
1089                 unsigned size = spill_size(t);
1090                 g(gen_address(ctx, dest_base, dest_offset, reg_is_fp(src_reg) ? IMM_PURPOSE_VLDR_VSTR_OFFSET : IMM_PURPOSE_STR_OFFSET, size));
1091                 gen_insn(INSN_MOV, size, 0, 0);
1092                 gen_address_offset();
1093                 gen_one(src_reg);
1094                 return true;
1095         }
1096         g(gen_memcpy_raw(ctx, dest_base, dest_offset, R_FRAME, (size_t)src_slot * slot_size, t->size, t->align));
1097         return true;
1100 static bool attr_w gen_memcpy_slots(struct codegen_context *ctx, frame_t dest_slot, frame_t src_slot)
1102         const struct type *t = get_type_of_local(ctx, src_slot);
1103         short dest_reg = ctx->registers[dest_slot];
1104         short src_reg = ctx->registers[src_slot];
1105         if (dest_reg >= 0 && src_reg >= 0) {
1106                 unsigned size = spill_size(t);
1107                 g(gen_mov(ctx, reg_is_fp(src_reg) ? size : OP_SIZE_NATIVE, dest_reg, src_reg));
1108                 return true;
1109         }
1110         if (dest_reg >= 0) {
1111                 unsigned size = spill_size(t);
1112                 g(gen_frame_load(ctx, size, garbage, src_slot, 0, false, dest_reg));
1113                 return true;
1114         }
1115         if (src_reg >= 0) {
1116                 unsigned size = spill_size(t);
1117                 g(gen_frame_store(ctx, size, dest_slot, 0, src_reg));
1118                 return true;
1119         }
1120         g(gen_memcpy_raw(ctx, R_FRAME, (size_t)dest_slot * slot_size, R_FRAME, (size_t)src_slot * slot_size, t->size, maximum(slot_size, t->align)));
1121         return true;
1124 static bool attr_w gen_clear_bitmap(struct codegen_context *ctx, unsigned additional_offset, unsigned dest_base, int64_t dest_offset, frame_t bitmap_slots)
1126         if (bitmap_slots <= INLINE_BITMAP_SLOTS) {
1127                 bool attr_unused scratch_2_zeroed = false;
1128                 size_t bitmap_length = (size_t)bitmap_slots * slot_size;
1129                 size_t clear_offset = 0;
1130                 additional_offset += (unsigned)dest_offset;
1131 #if defined(ARCH_X86)
1132                 g(gen_3address_alu(ctx, OP_SIZE_4, ALU_XOR, R_SCRATCH_1, R_SCRATCH_1, R_SCRATCH_1, 0));
1133 #endif
1134 #if defined(ARCH_ARM32) || defined(ARCH_S390)
1135                 g(gen_load_constant(ctx, R_SCRATCH_1, 0));
1136 #endif
1137                 while (clear_offset < bitmap_length) {
1138                         size_t len = bitmap_length - clear_offset;
1139                         if (len > frame_align)
1140                                 len = frame_align;
1141                         if (additional_offset)
1142                                 len = minimum(len, additional_offset & -additional_offset);
1143 #if defined(ARCH_ARM32) || defined(ARCH_S390)
1144                         len = minimum(len, 2U << OP_SIZE_NATIVE);
1145                         if (len == 2U << OP_SIZE_NATIVE) {
1146                                 if (!scratch_2_zeroed) {
1147                                         g(gen_load_constant(ctx, R_SCRATCH_2, 0));
1148                                         scratch_2_zeroed = true;
1149                                 }
1150                                 g(gen_address(ctx, dest_base, dest_offset + clear_offset, IMM_PURPOSE_LDP_STP_OFFSET, OP_SIZE_NATIVE));
1151                                 gen_insn(INSN_STP, OP_SIZE_NATIVE, 0, 0);
1152                                 gen_address_offset();
1153                                 gen_one(R_SCRATCH_1);
1154                                 gen_one(R_SCRATCH_2);
1155                                 goto next_loop;
1156                         }
1157 #elif defined(ARCH_ARM64)
1158                         len = minimum(len, 1U << OP_SIZE_16);
1159                         if (len == 1U << OP_SIZE_16) {
1160                                 g(gen_address(ctx, dest_base, dest_offset + clear_offset, IMM_PURPOSE_LDP_STP_OFFSET, OP_SIZE_8));
1161                                 g(gen_imm(ctx, 0, IMM_PURPOSE_STORE_VALUE, OP_SIZE_8));
1162                                 gen_insn(INSN_STP, OP_SIZE_NATIVE, 0, 0);
1163                                 gen_address_offset();
1164                                 gen_imm_offset();
1165                                 gen_imm_offset();
1166                                 goto next_loop;
1167                         }
1168 #elif defined(ARCH_X86)
1169                         len = minimum(len, 1U << OP_SIZE_16);
1170                         if (len == 1U << OP_SIZE_16 && cpu_test_feature(CPU_FEATURE_sse)) {
1171                                 if (!scratch_2_zeroed) {
1172                                         g(gen_3address_alu(ctx, OP_SIZE_16, ALU_XOR, FR_SCRATCH_1, FR_SCRATCH_1, FR_SCRATCH_1, 0));
1173                                         scratch_2_zeroed = true;
1174                                 }
1175                                 g(gen_address(ctx, dest_base, dest_offset + clear_offset, IMM_PURPOSE_VLDR_VSTR_OFFSET, OP_SIZE_16));
1176                                 gen_insn(INSN_MOV, OP_SIZE_16, 0, 0);
1177                                 gen_address_offset();
1178                                 gen_one(FR_SCRATCH_1);
1179                                 goto next_loop;
1180                         }
1181 #endif
1182                         len = minimum(len, 1U << OP_SIZE_NATIVE);
1183                         len = (size_t)1 << high_bit(len);
1184 #if defined(ARCH_X86) || defined(ARCH_ARM32) || defined(ARCH_S390)
1185                         g(gen_address(ctx, dest_base, dest_offset + clear_offset, IMM_PURPOSE_STR_OFFSET, log_2(len)));
1186                         gen_insn(INSN_MOV, log_2(len), 0, 0);
1187                         gen_address_offset();
1188                         gen_one(R_SCRATCH_1);
1189 #else
1190                         g(gen_address(ctx, dest_base, dest_offset + clear_offset, IMM_PURPOSE_STR_OFFSET, log_2(len)));
1191                         g(gen_imm(ctx, 0, IMM_PURPOSE_STORE_VALUE, log_2(len)));
1192                         gen_insn(INSN_MOV, log_2(len), 0, 0);
1193                         gen_address_offset();
1194                         gen_imm_offset();
1195 #endif
1196                         goto next_loop;
1197 next_loop:
1198                         clear_offset += len;
1199                         additional_offset += len;
1200                 }
1201                 return true;
1202         }
1203 #if (defined(ARCH_X86_64) || defined(ARCH_X86_X32)) && !defined(ARCH_X86_WIN_ABI)
1204         if (cpu_test_feature(CPU_FEATURE_erms)) {
1205                 g(gen_x86_push(ctx, R_DI, true));
1207                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, R_DI, dest_base, dest_offset, 0));
1209                 g(gen_load_constant(ctx, R_CX, (size_t)bitmap_slots * slot_size));
1211                 g(gen_3address_alu(ctx, OP_SIZE_4, ALU_XOR, R_AX, R_AX, R_AX, 0));
1213                 gen_insn(INSN_MEMSET, 0, 0, 0);
1214                 gen_one(ARG_ADDRESS_1_POST_I);
1215                 gen_one(R_DI);
1216                 gen_eight(0);
1217                 gen_one(R_CX);
1218                 gen_one(R_AX);
1220                 g(gen_x86_pop(ctx, R_DI, true));
1222                 return true;
1223         }
1224 #endif
1225         g(gen_upcall_start(ctx, offsetof(struct cg_upcall_vector_s, mem_clear), 2));
1227         g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, R_ARG0, dest_base, dest_offset, 0));
1228         g(gen_upcall_argument(ctx, 0));
1230         g(gen_load_constant(ctx, R_ARG1, (size_t)bitmap_slots * slot_size));
1231         g(gen_upcall_argument(ctx, 1));
1233         g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, mem_clear), 2));
1235         return true;
1238 static bool attr_w load_function_offset(struct codegen_context *ctx, unsigned dest, size_t fn_offset)
1240         g(gen_frame_load_raw(ctx, OP_SIZE_ADDRESS, zero_x, 0, frame_offs(function), false, dest));
1242         g(gen_address(ctx, dest, fn_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
1243         gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
1244         gen_one(dest);
1245         gen_address_offset();
1247         return true;