ssa: don't swap the arguments of comparison operators
[ajla.git] / cg-ptr.inc
blobbf80aeb77091ad051cfdd9afb52d0e45ad822afc
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 #if defined(POINTER_COMPRESSION)
20 #define POINTER_THUNK_BIT               0
21 #elif defined(POINTER_IGNORE_START)
22 #define POINTER_THUNK_BIT               POINTER_IGNORE_TOP_BIT
23 #elif defined(POINTER_TAG)
24 #define POINTER_THUNK_BIT               POINTER_TAG_BIT
25 #else
26 unsupported pointer mode
27 #endif
29 static bool attr_w gen_ptr_is_thunk(struct codegen_context *ctx, unsigned reg, frame_t slot, uint32_t label)
31         if (slot != NO_FRAME_T) {
32                 if (ctx->flag_cache[slot] & FLAG_CACHE_IS_NOT_THUNK || da(ctx->fn,function)->local_variables_flags[slot].must_be_data)
33                         return true;
34                 ctx->flag_cache[slot] |= FLAG_CACHE_IS_NOT_THUNK;
35         }
36 #if defined(ARCH_X86)
37         if (POINTER_THUNK_BIT < 8
38 #if defined(ARCH_X86_32)
39                 && reg < 4
40 #endif
41                 ) {
42                 g(gen_cmp_test_imm_jmp(ctx, INSN_TEST, OP_SIZE_1, reg, (uint64_t)1 << POINTER_THUNK_BIT, COND_NE, label));
43         } else
44 #endif
45         {
46                 g(gen_cmp_test_imm_jmp(ctx, INSN_TEST, OP_SIZE_SLOT, reg, (uint64_t)1 << POINTER_THUNK_BIT, COND_NE, label));
47         }
48         return true;
51 static bool attr_w gen_test_multiple_thunks(struct codegen_context *ctx, frame_t *variables, size_t n_variables, uint32_t label)
53         size_t n;
54 #if defined(ARCH_X86) || defined(ARCH_S390)
55         if (n_variables >= 2) {
56                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, variables[0], 0, false, R_SCRATCH_1));
57                 for (n = 1; n < n_variables; n++) {
58                         g(gen_frame_load_op(ctx, OP_SIZE_SLOT, garbage, ALU_OR, 0, variables[n], 0, false, R_SCRATCH_1));
59                 }
60                 g(gen_ptr_is_thunk(ctx, R_SCRATCH_1, NO_FRAME_T, label));
61                 return true;
62         }
63 #endif
64         for (n = 0; n < n_variables; n++) {
65                 unsigned reg;
66                 g(gen_frame_get(ctx, OP_SIZE_SLOT, garbage, variables[n], R_SCRATCH_1, &reg));
67                 g(gen_ptr_is_thunk(ctx, reg, NO_FRAME_T, label));
68         }
69         return true;
72 static bool attr_w gen_test_variables(struct codegen_context *ctx, frame_t *variables, size_t n_variables, uint32_t label)
74         size_t i;
75         frame_t *vars;
77         for (i = 0; i < n_variables; i++) {
78                 frame_t v = variables[i];
79                 if (slot_is_register(ctx, v))
80                         g(unspill(ctx, v));
81         }
83         vars = mem_alloc_array_mayfail(mem_alloc_mayfail, frame_t *, 0, 0, n_variables, sizeof(frame_t), &ctx->err);
84         if (unlikely(!vars))
85                 return false;
87         for (i = 0; i < 2; i++) {
88                 size_t n_vars = 0;
89                 size_t n;
90                 for (n = 0; n < n_variables; n++) {
91                         frame_t v = variables[n];
92                         if (!i ? da(ctx->fn,function)->local_variables_flags[v].must_be_flat : da(ctx->fn,function)->local_variables_flags[v].must_be_data)
93                                 vars[n_vars++] = v;
94                 }
95                 if (!i) {
96                         if (!gen_test_multiple(ctx, vars, n_vars, label)) {
97                                 mem_free(vars);
98                                 return false;
99                         }
100                 } else {
101                         if (!gen_test_multiple_thunks(ctx, vars, n_vars, label)) {
102                                 mem_free(vars);
103                                 return false;
104                         }
105                 }
106         }
108         mem_free(vars);
109         return true;
112 static bool attr_w gen_barrier(struct codegen_context *ctx)
114         if (ARCH_NEEDS_BARRIER)
115                 gen_insn(INSN_MB, 0, 0, 0);
116         return true;
119 static bool attr_w gen_compare_refcount(struct codegen_context *ctx, unsigned ptr, unsigned val, unsigned cond, uint32_t label)
121         unsigned op_size = log_2(sizeof(refcount_int_t));
122 #if defined(ARCH_X86)
123         bool logical = COND_IS_LOGICAL(cond);
124         g(gen_address(ctx, ptr, offsetof(struct data, refcount_), IMM_PURPOSE_LDR_OFFSET, op_size));
125         g(gen_imm(ctx, val, IMM_PURPOSE_CMP, op_size));
126         gen_insn(INSN_CMP, op_size, 0, 1 + logical);
127         gen_address_offset();
128         gen_imm_offset();
130         gen_insn(!logical ? INSN_JMP_COND : INSN_JMP_COND_LOGICAL, op_size, cond, 0);
131         gen_four(label);
132 #else
133         g(gen_address(ctx, ptr, offsetof(struct data, refcount_), IMM_PURPOSE_LDR_OFFSET, op_size));
134         gen_insn(INSN_MOV, op_size, 0, 0);
135         gen_one(R_SCRATCH_2);
136         gen_address_offset();
138         g(gen_cmp_test_imm_jmp(ctx, INSN_CMP, op_size, R_SCRATCH_2, val, cond, label));
139 #endif
140         return true;
143 static bool attr_w gen_compare_ptr_tag(struct codegen_context *ctx, unsigned reg, unsigned tag, unsigned cond, uint32_t label, unsigned tmp_reg)
145 #if defined(ARCH_S390)
146         switch (cond) {
147                 case COND_A:    cond = COND_G; break;
148                 case COND_AE:   cond = COND_GE; break;
149                 case COND_B:    cond = COND_L; break;
150                 case COND_BE:   cond = COND_LE; break;
151         }
152 #endif
153 #if defined(DATA_TAG_AT_ALLOC)
154         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHR, tmp_reg, reg, POINTER_IGNORE_START, false));
155 #elif defined(REFCOUNT_TAG)
156         size_t offset = offsetof(struct data, refcount_);
157 #if defined(C_BIG_ENDIAN)
158         offset += sizeof(refcount_int_t) - 1;
159 #endif
160 #if defined(ARCH_X86) && REFCOUNT_STEP == 256
161         g(gen_imm(ctx, tag, IMM_PURPOSE_CMP, OP_SIZE_4));
162         gen_insn(INSN_CMP, OP_SIZE_1, 0, 1);
163         gen_one(ARG_ADDRESS_1);
164         gen_one(reg);
165         gen_eight(offset);
166         gen_imm_offset();
168         gen_insn(INSN_JMP_COND, OP_SIZE_1, cond, 0);
169         gen_four(label);
170         return true;
171 #endif
172         if (ARCH_HAS_BWX && REFCOUNT_STEP == 256
173 #if defined(ARCH_S390)
174                 && cpu_test_feature(CPU_FEATURE_extended_imm)
175 #endif
176             ) {
177                 gen_insn(INSN_MOV, OP_SIZE_1, 0, 0);
178                 gen_one(tmp_reg);
179                 gen_one(ARG_ADDRESS_1);
180                 gen_one(reg);
181                 gen_eight(offset);
182         } else {
183                 gen_insn(INSN_MOV, log_2(sizeof(refcount_int_t)), 0, 0);
184                 gen_one(tmp_reg);
185                 gen_one(ARG_ADDRESS_1);
186                 gen_one(reg);
187                 gen_eight(offsetof(struct data, refcount_));
189                 g(gen_3address_alu_imm(ctx, log_2(sizeof(refcount_int_t)), ALU_AND, tmp_reg, tmp_reg, REFCOUNT_STEP - 1, 0));
190         }
191 #else
192 #if defined(ARCH_S390)
193         if (sizeof(tag_t) == 1 && !cpu_test_feature(CPU_FEATURE_extended_imm)) {
194                 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
195                 gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_0_8, 0);
196                 gen_one(tmp_reg);
197                 gen_one(tmp_reg);
198                 gen_address_offset();
200                 g(gen_extend(ctx, log_2(sizeof(tag_t)), zero_x, tmp_reg, tmp_reg));
201         } else
202 #endif
203         {
204                 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
205                 gen_insn(INSN_MOV, log_2(sizeof(tag_t)), 0, 0);
206                 gen_one(tmp_reg);
207                 gen_address_offset();
208         }
209 #endif
210         g(gen_cmp_test_imm_jmp(ctx, INSN_CMP, i_size(OP_SIZE_4), tmp_reg, tag, cond, label));
211         return true;
214 static bool attr_w gen_compare_da_tag(struct codegen_context *ctx, unsigned reg, unsigned tag, unsigned cond, uint32_t label, unsigned tmp_reg)
216 #if defined(ARCH_S390)
217         switch (cond) {
218                 case COND_A:    cond = COND_G; break;
219                 case COND_AE:   cond = COND_GE; break;
220                 case COND_B:    cond = COND_L; break;
221                 case COND_BE:   cond = COND_LE; break;
222         }
223 #endif
224 #if defined(POINTER_COMPRESSION)
225 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3 && defined(REFCOUNT_TAG) && REFCOUNT_STEP == 256 && defined(C_LITTLE_ENDIAN)
226         g(gen_imm(ctx, tag, IMM_PURPOSE_CMP, log_2(sizeof(tag_t))));
227         gen_insn(INSN_CMP, log_2(sizeof(tag_t)), 0, 0);
228         gen_one(ARG_ADDRESS_1 + POINTER_COMPRESSION);
229         gen_one(reg);
230         gen_eight(offsetof(struct data, refcount_));
231         gen_imm_offset();
233         gen_insn(INSN_JMP_COND, OP_SIZE_4, cond, 0);
234         gen_four(label);
236         return true;
237 #endif
238         if (ARCH_PREFERS_SX(OP_SIZE_4)) {
239                 g(gen_extend(ctx, OP_SIZE_4, zero_x, tmp_reg, reg));
241                 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, tmp_reg, POINTER_COMPRESSION, false));
242         } else {
243                 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, reg, POINTER_COMPRESSION, false));
244         }
245         g(gen_compare_ptr_tag(ctx, tmp_reg, tag, cond, label, tmp_reg));
246         return true;
247 #endif
248         g(gen_compare_ptr_tag(ctx, reg, tag, cond, label, tmp_reg));
249         return true;
252 static bool attr_w gen_compare_tag_and_refcount(struct codegen_context *ctx, unsigned reg, unsigned tag, uint32_t label, unsigned attr_unused tmp_reg)
254 #if defined(REFCOUNT_TAG)
255         g(gen_compare_refcount(ctx, reg, tag, COND_NE, label));
256 #else
257         g(gen_compare_ptr_tag(ctx, reg, tag, COND_NE, label, tmp_reg));
258         g(gen_compare_refcount(ctx, reg, REFCOUNT_STEP, COND_AE, label));
259 #endif
260         return true;
263 static bool attr_w gen_decompress_pointer(struct codegen_context *ctx, bool attr_unused zx, unsigned dest, unsigned src, int64_t offset)
265 #ifdef POINTER_COMPRESSION
266 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3
267         if (offset || dest != src) {
268                 g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
269                 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, is_imm()));
270                 gen_one(dest);
271                 gen_one(ARG_SHIFTED_REGISTER);
272                 gen_one(ARG_SHIFT_LSL | POINTER_COMPRESSION);
273                 gen_one(src);
274                 gen_imm_offset();
275                 return true;
276         }
277 #endif
278         if (zx) {
279                 g(gen_extend(ctx, OP_SIZE_4, zero_x, dest, src));
280                 src = dest;
281         }
282         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, dest, src, POINTER_COMPRESSION, false));
283         src = dest;
284 #endif
285         if (offset)
286                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, src, offset, 0));
287         return true;
290 static bool attr_w gen_compress_pointer(struct codegen_context attr_unused *ctx, unsigned dest, unsigned src)
292 #ifdef POINTER_COMPRESSION
293         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHR, dest, src, POINTER_COMPRESSION, false));
294 #else
295         g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), dest, src));
296 #endif
297         return true;
300 static bool attr_w gen_frame_get_pointer(struct codegen_context *ctx, frame_t slot, bool deref, unsigned dest)
302         if (!deref) {
303                 g(gen_upcall_start(ctx, 1));
304                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, false, R_ARG0));
305                 g(gen_upcall_argument(ctx, 0));
306                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
307                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, false, dest));
308         } else if (!da(ctx->fn,function)->local_variables_flags[slot].may_be_borrowed) {
309                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, false, dest));
310                 g(gen_set_1(ctx, R_FRAME, slot, 0, false));
311                 flag_set(ctx, slot, false);
312         } else {
313                 uint32_t skip_label;
314                 skip_label = alloc_label(ctx);
315                 if (unlikely(!skip_label))
316                         return false;
317                 if (flag_is_set(ctx, slot)) {
318                         g(gen_set_1(ctx, R_FRAME, slot, 0, false));
319                         goto move_it;
320                 }
321                 if (flag_is_clear(ctx, slot))
322                         goto do_reference;
323                 g(gen_test_1(ctx, R_FRAME, slot, 0, skip_label, false, TEST_CLEAR));
324 do_reference:
325                 g(gen_upcall_start(ctx, 1));
326                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, false, R_ARG0));
327                 g(gen_upcall_argument(ctx, 0));
328                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
329 move_it:
330                 gen_label(skip_label);
331                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, false, dest));
332                 g(gen_frame_clear(ctx, OP_SIZE_SLOT, slot));
333                 flag_set(ctx, slot, false);
334         }
335         return true;
338 static bool attr_w gen_frame_set_pointer(struct codegen_context *ctx, frame_t slot, unsigned src, bool reference, bool not_thunk)
340         bool escape_on_thunk = !not_thunk && da(ctx->fn,function)->local_variables_flags[slot].must_be_data;
341         g(gen_set_1(ctx, R_FRAME, slot, 0, true));
342         flag_set_unknown(ctx, slot);
343         if (not_thunk)
344                 ctx->flag_cache[slot] |= FLAG_CACHE_IS_NOT_THUNK;
345         flag_set(ctx, slot, true);
346         g(gen_frame_store(ctx, OP_SIZE_SLOT, slot, 0, src));
347         if (reference) {
348                 if (escape_on_thunk)
349                         g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_SAVED_1, src));
350                 g(gen_upcall_start(ctx, 1));
351                 g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_ARG0, src));
352                 g(gen_upcall_argument(ctx, 0));
353                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
354                 if (escape_on_thunk)
355                         src = R_SAVED_1;
356         }
357         if (escape_on_thunk) {
358                 uint32_t nondata_label = alloc_escape_label_for_ip(ctx, ctx->current_position);
359                 if (unlikely(!nondata_label))
360                         return false;
361                 g(gen_ptr_is_thunk(ctx, src, NO_FRAME_T, nondata_label));
362         }
363         return true;
366 static bool attr_w gen_frame_set_pointer_2(struct codegen_context *ctx, frame_t slot_r, unsigned src, unsigned flags, uint32_t escape_label)
368         g(gen_ptr_is_thunk(ctx, src, NO_FRAME_T, escape_label));
369         g(gen_barrier(ctx));
371         if (flags & OPCODE_STRUCT_MAY_BORROW) {
372                 g(gen_frame_store(ctx, OP_SIZE_SLOT, slot_r, 0, src));
373                 flag_set(ctx, slot_r, false);
374         } else {
375                 g(gen_frame_set_pointer(ctx, slot_r, src, true, true));
376         }
377         return true;
380 static bool attr_w gen_frame_load_slot(struct codegen_context *ctx, frame_t slot, unsigned reg)
382         g(gen_frame_load(ctx, OP_SIZE_SLOT, native, slot, 0, false, reg));
383         return true;
386 static bool attr_w gen_frame_get_slot(struct codegen_context *ctx, frame_t slot, unsigned reg, unsigned *dest)
388         if (slot_is_register(ctx, slot)) {
389                 *dest = ctx->registers[slot];
390                 return true;
391         }
392         *dest = reg;
393         g(gen_frame_load_slot(ctx, slot, reg));
394         return true;
397 static bool attr_w gen_frame_decompress_slot(struct codegen_context *ctx, frame_t slot, unsigned reg, unsigned *dest, uint32_t escape_label)
399         if (slot_is_register(ctx, slot)) {
400                 unsigned creg = ctx->registers[slot];
401                 g(gen_ptr_is_thunk(ctx, creg, slot, escape_label));
402 #if !defined(POINTER_COMPRESSION)
403                 *dest = creg;
404 #else
405                 g(gen_decompress_pointer(ctx, ARCH_PREFERS_SX(OP_SIZE_SLOT), reg, creg, 0));
406                 *dest = reg;
407 #endif
408                 return true;
409         }
410         g(gen_frame_load(ctx, OP_SIZE_SLOT, native, slot, 0, false, reg));
411         g(gen_ptr_is_thunk(ctx, reg, slot, escape_label));
412         g(gen_decompress_pointer(ctx, ARCH_PREFERS_SX(OP_SIZE_SLOT), reg, reg, 0));
413         *dest = reg;
414         return true;