codegen: allocate registers for unknown or record variables
[ajla.git] / cg-ptr.inc
blob733d4ea03f03a23fcdf345c636a39ff3fa747183
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, 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, 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                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, variables[n], 0, R_SCRATCH_1));
66                 g(gen_ptr_is_thunk(ctx, R_SCRATCH_1, NO_FRAME_T, label));
67         }
68         return true;
71 static bool attr_w gen_barrier(struct codegen_context *ctx)
73         if (ARCH_NEEDS_BARRIER)
74                 gen_insn(INSN_MB, 0, 0, 0);
75         return true;
78 static bool attr_w gen_compare_refcount(struct codegen_context *ctx, unsigned ptr, unsigned val, unsigned cond, uint32_t label)
80         unsigned op_size = log_2(sizeof(refcount_int_t));
81 #if defined(ARCH_X86)
82         bool logical = COND_IS_LOGICAL(cond);
83         g(gen_address(ctx, ptr, offsetof(struct data, refcount_), IMM_PURPOSE_LDR_OFFSET, op_size));
84         g(gen_imm(ctx, val, IMM_PURPOSE_CMP, op_size));
85         gen_insn(INSN_CMP, op_size, 0, 1 + logical);
86         gen_address_offset();
87         gen_imm_offset();
89         gen_insn(!logical ? INSN_JMP_COND : INSN_JMP_COND_LOGICAL, op_size, cond, 0);
90         gen_four(label);
91 #else
92         g(gen_address(ctx, ptr, offsetof(struct data, refcount_), IMM_PURPOSE_LDR_OFFSET, op_size));
93         gen_insn(INSN_MOV, op_size, 0, 0);
94         gen_one(R_SCRATCH_2);
95         gen_address_offset();
97         g(gen_cmp_test_imm_jmp(ctx, INSN_CMP, op_size, R_SCRATCH_2, val, cond, label));
98 #endif
99         return true;
102 static bool attr_w gen_compare_ptr_tag(struct codegen_context *ctx, unsigned reg, unsigned tag, unsigned cond, uint32_t label, unsigned tmp_reg)
104 #if defined(ARCH_S390)
105         switch (cond) {
106                 case COND_A:    cond = COND_G; break;
107                 case COND_AE:   cond = COND_GE; break;
108                 case COND_B:    cond = COND_L; break;
109                 case COND_BE:   cond = COND_LE; break;
110         }
111 #endif
112 #if defined(DATA_TAG_AT_ALLOC)
113         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHR, tmp_reg, reg, POINTER_IGNORE_START, false));
114 #elif defined(REFCOUNT_TAG)
115         size_t offset = offsetof(struct data, refcount_);
116 #if defined(C_BIG_ENDIAN)
117         offset += sizeof(refcount_t) - 1;
118 #endif
119 #if defined(ARCH_X86) && REFCOUNT_STEP == 256
120         g(gen_imm(ctx, tag, IMM_PURPOSE_CMP, OP_SIZE_4));
121         gen_insn(INSN_CMP, OP_SIZE_1, 0, 1);
122         gen_one(ARG_ADDRESS_1);
123         gen_one(reg);
124         gen_eight(offset);
125         gen_imm_offset();
127         gen_insn(INSN_JMP_COND, OP_SIZE_1, cond, 0);
128         gen_four(label);
129         return true;
130 #endif
131         if (ARCH_HAS_BWX && REFCOUNT_STEP == 256) {
132                 gen_insn(INSN_MOV, OP_SIZE_1, 0, 0);
133                 gen_one(tmp_reg);
134                 gen_one(ARG_ADDRESS_1);
135                 gen_one(reg);
136                 gen_eight(offset);
137         } else {
138                 gen_insn(INSN_MOV, log_2(sizeof(refcount_int_t)), 0, 0);
139                 gen_one(tmp_reg);
140                 gen_one(ARG_ADDRESS_1);
141                 gen_one(reg);
142                 gen_eight(offset);
144                 g(gen_3address_alu_imm(ctx, log_2(sizeof(refcount_int_t)), ALU_AND, tmp_reg, tmp_reg, REFCOUNT_STEP - 1, 0));
145         }
146 #else
147 #if defined(ARCH_S390)
148         if (sizeof(tag_t) == 1 && !cpu_test_feature(CPU_FEATURE_long_displacement)) {
149                 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
150                 gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_0_8, 0);
151                 gen_one(tmp_reg);
152                 gen_one(tmp_reg);
153                 gen_address_offset();
155                 g(gen_extend(ctx, log_2(sizeof(tag_t)), zero_x, tmp_reg, tmp_reg));
156         } else
157 #endif
158         {
159                 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
160                 gen_insn(INSN_MOV, log_2(sizeof(tag_t)), 0, 0);
161                 gen_one(tmp_reg);
162                 gen_address_offset();
163         }
164 #endif
165         g(gen_cmp_test_imm_jmp(ctx, INSN_CMP, i_size(OP_SIZE_4), tmp_reg, tag, cond, label));
166         return true;
169 static bool attr_w gen_compare_da_tag(struct codegen_context *ctx, unsigned reg, unsigned tag, unsigned cond, uint32_t label, unsigned tmp_reg)
171 #if defined(ARCH_S390)
172         switch (cond) {
173                 case COND_A:    cond = COND_G; break;
174                 case COND_AE:   cond = COND_GE; break;
175                 case COND_B:    cond = COND_L; break;
176                 case COND_BE:   cond = COND_LE; break;
177         }
178 #endif
179 #if defined(POINTER_COMPRESSION)
180 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3 && defined(REFCOUNT_TAG) && REFCOUNT_STEP == 256 && defined(C_LITTLE_ENDIAN)
181         g(gen_imm(ctx, tag, IMM_PURPOSE_CMP, log_2(sizeof(tag_t))));
182         gen_insn(INSN_CMP, log_2(sizeof(tag_t)), 0, 0);
183         gen_one(ARG_ADDRESS_1 + POINTER_COMPRESSION);
184         gen_one(reg);
185         gen_eight(offsetof(struct data, refcount_));
186         gen_imm_offset();
188         gen_insn(INSN_JMP_COND, OP_SIZE_4, cond, 0);
189         gen_four(label);
191         return true;
192 #endif
193         if (ARCH_PREFERS_SX(OP_SIZE_4)) {
194                 g(gen_extend(ctx, OP_SIZE_4, zero_x, tmp_reg, reg));
196                 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, tmp_reg, POINTER_COMPRESSION, false));
197         } else {
198                 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, reg, POINTER_COMPRESSION, false));
199         }
200         g(gen_compare_ptr_tag(ctx, tmp_reg, tag, cond, label, tmp_reg));
201         return true;
202 #endif
203         g(gen_compare_ptr_tag(ctx, reg, tag, cond, label, tmp_reg));
204         return true;
207 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)
209 #if defined(REFCOUNT_TAG)
210         g(gen_compare_refcount(ctx, reg, tag, COND_NE, label));
211 #else
212         g(gen_compare_ptr_tag(ctx, reg, tag, COND_NE, label, tmp_reg));
213         g(gen_compare_refcount(ctx, reg, REFCOUNT_STEP, COND_AE, label));
214 #endif
215         return true;
218 static bool attr_w gen_decompress_pointer(struct codegen_context *ctx, bool attr_unused zx, unsigned reg, int64_t offset)
220 #ifdef POINTER_COMPRESSION
221 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3
222         if (offset) {
223                 g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
224                 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, is_imm()));
225                 gen_one(reg);
226                 gen_one(ARG_SHIFTED_REGISTER);
227                 gen_one(ARG_SHIFT_LSL | POINTER_COMPRESSION);
228                 gen_one(reg);
229                 gen_imm_offset();
230                 return true;
231         }
232 #endif
233         if (zx)
234                 g(gen_extend(ctx, OP_SIZE_4, zero_x, reg, reg));
235         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, reg, reg, POINTER_COMPRESSION, false));
236 #endif
237         if (offset)
238                 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, reg, reg, offset, 0));
239         return true;
242 static bool attr_w gen_compress_pointer(struct codegen_context attr_unused *ctx, unsigned attr_unused reg)
244 #ifdef POINTER_COMPRESSION
245         g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHR, reg, reg, POINTER_COMPRESSION, false));
246 #endif
247         return true;
250 static bool attr_w gen_frame_get_pointer(struct codegen_context *ctx, frame_t slot, bool deref, unsigned dest)
252         if (!deref) {
253                 g(gen_upcall_start(ctx, 1));
254                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, R_ARG0));
255                 g(gen_upcall_argument(ctx, 0));
256                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
257                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
258         } else if (!da(ctx->fn,function)->local_variables_flags[slot].may_be_borrowed) {
259                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
260                 g(gen_set_1(ctx, R_FRAME, slot, 0, false));
261                 flag_set(ctx, slot, false);
262         } else {
263                 uint32_t skip_label;
264                 skip_label = alloc_label(ctx);
265                 if (unlikely(!skip_label))
266                         return false;
267                 if (flag_is_set(ctx, slot)) {
268                         g(gen_set_1(ctx, R_FRAME, slot, 0, false));
269                         goto move_it;
270                 }
271                 if (flag_is_clear(ctx, slot))
272                         goto do_reference;
273                 g(gen_test_1(ctx, R_FRAME, slot, 0, skip_label, false, TEST_CLEAR));
274 do_reference:
275                 g(gen_upcall_start(ctx, 1));
276                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, R_ARG0));
277                 g(gen_upcall_argument(ctx, 0));
278                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
279 move_it:
280                 gen_label(skip_label);
281                 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
282                 g(gen_frame_clear(ctx, OP_SIZE_SLOT, slot));
283                 flag_set(ctx, slot, false);
284         }
285         return true;
288 static bool attr_w gen_frame_set_pointer(struct codegen_context *ctx, frame_t slot, unsigned src, bool reference, bool not_thunk)
290         bool escape_on_thunk = !not_thunk && da(ctx->fn,function)->local_variables_flags[slot].must_be_data;
291         g(gen_set_1(ctx, R_FRAME, slot, 0, true));
292         flag_set_unknown(ctx, slot);
293         if (not_thunk)
294                 ctx->flag_cache[slot] |= FLAG_CACHE_IS_NOT_THUNK;
295         flag_set(ctx, slot, true);
296         g(gen_frame_store(ctx, OP_SIZE_SLOT, slot, 0, src));
297         if (reference) {
298                 if (escape_on_thunk)
299                         g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_SAVED_1, src));
300                 g(gen_upcall_start(ctx, 1));
301                 g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_ARG0, src));
302                 g(gen_upcall_argument(ctx, 0));
303                 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
304                 if (escape_on_thunk)
305                         src = R_SAVED_1;
306         }
307         if (escape_on_thunk) {
308                 uint32_t nondata_label = alloc_escape_label_for_ip(ctx, ctx->current_position);
309                 if (unlikely(!nondata_label))
310                         return false;
311                 g(gen_ptr_is_thunk(ctx, src, NO_FRAME_T, nondata_label));
312         }
313         return true;