2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
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
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.
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/>.
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
26 unsupported pointer mode
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)
34 ctx->flag_cache[slot] |= FLAG_CACHE_IS_NOT_THUNK;
37 if (POINTER_THUNK_BIT < 8
38 #if defined(ARCH_X86_32)
42 g(gen_cmp_test_imm_jmp(ctx, INSN_TEST, OP_SIZE_1, reg, (uint64_t)1 << POINTER_THUNK_BIT, COND_NE, label));
46 g(gen_cmp_test_imm_jmp(ctx, INSN_TEST, OP_SIZE_SLOT, reg, (uint64_t)1 << POINTER_THUNK_BIT, COND_NE, label));
51 static bool attr_w gen_test_multiple_thunks(struct codegen_context *ctx, frame_t *variables, size_t n_variables, uint32_t label)
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));
60 g(gen_ptr_is_thunk(ctx, R_SCRATCH_1, NO_FRAME_T, label));
64 for (n = 0; n < n_variables; n++) {
66 g(gen_frame_get(ctx, OP_SIZE_SLOT, garbage, variables[n], 0, R_SCRATCH_1, ®));
67 g(gen_ptr_is_thunk(ctx, reg, NO_FRAME_T, label));
72 static bool attr_w gen_test_variables(struct codegen_context *ctx, frame_t *variables, size_t n_variables, uint32_t label)
77 for (i = 0; i < n_variables; i++) {
78 frame_t v = variables[i];
79 if (ctx->registers[v] >= 0)
83 vars = mem_alloc_array_mayfail(mem_alloc_mayfail, frame_t *, 0, 0, n_variables, sizeof(frame_t), &ctx->err);
87 for (i = 0; i < 2; i++) {
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)
96 if (!gen_test_multiple(ctx, vars, n_vars, label)) {
101 if (!gen_test_multiple_thunks(ctx, vars, n_vars, label)) {
112 static bool attr_w gen_barrier(struct codegen_context *ctx)
114 if (ARCH_NEEDS_BARRIER)
115 gen_insn(INSN_MB, 0, 0, 0);
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();
130 gen_insn(!logical ? INSN_JMP_COND : INSN_JMP_COND_LOGICAL, op_size, cond, 0);
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));
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)
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;
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_t) - 1;
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);
168 gen_insn(INSN_JMP_COND, OP_SIZE_1, cond, 0);
172 if (ARCH_HAS_BWX && REFCOUNT_STEP == 256) {
173 gen_insn(INSN_MOV, OP_SIZE_1, 0, 0);
175 gen_one(ARG_ADDRESS_1);
179 gen_insn(INSN_MOV, log_2(sizeof(refcount_int_t)), 0, 0);
181 gen_one(ARG_ADDRESS_1);
185 g(gen_3address_alu_imm(ctx, log_2(sizeof(refcount_int_t)), ALU_AND, tmp_reg, tmp_reg, REFCOUNT_STEP - 1, 0));
188 #if defined(ARCH_S390)
189 if (sizeof(tag_t) == 1 && !cpu_test_feature(CPU_FEATURE_long_displacement)) {
190 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
191 gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_0_8, 0);
194 gen_address_offset();
196 g(gen_extend(ctx, log_2(sizeof(tag_t)), zero_x, tmp_reg, tmp_reg));
200 g(gen_address(ctx, reg, offsetof(struct data, tag), IMM_PURPOSE_LDR_OFFSET, log_2(sizeof(tag_t))));
201 gen_insn(INSN_MOV, log_2(sizeof(tag_t)), 0, 0);
203 gen_address_offset();
206 g(gen_cmp_test_imm_jmp(ctx, INSN_CMP, i_size(OP_SIZE_4), tmp_reg, tag, cond, label));
210 static bool attr_w gen_compare_da_tag(struct codegen_context *ctx, unsigned reg, unsigned tag, unsigned cond, uint32_t label, unsigned tmp_reg)
212 #if defined(ARCH_S390)
214 case COND_A: cond = COND_G; break;
215 case COND_AE: cond = COND_GE; break;
216 case COND_B: cond = COND_L; break;
217 case COND_BE: cond = COND_LE; break;
220 #if defined(POINTER_COMPRESSION)
221 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3 && defined(REFCOUNT_TAG) && REFCOUNT_STEP == 256 && defined(C_LITTLE_ENDIAN)
222 g(gen_imm(ctx, tag, IMM_PURPOSE_CMP, log_2(sizeof(tag_t))));
223 gen_insn(INSN_CMP, log_2(sizeof(tag_t)), 0, 0);
224 gen_one(ARG_ADDRESS_1 + POINTER_COMPRESSION);
226 gen_eight(offsetof(struct data, refcount_));
229 gen_insn(INSN_JMP_COND, OP_SIZE_4, cond, 0);
234 if (ARCH_PREFERS_SX(OP_SIZE_4)) {
235 g(gen_extend(ctx, OP_SIZE_4, zero_x, tmp_reg, reg));
237 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, tmp_reg, POINTER_COMPRESSION, false));
239 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, tmp_reg, reg, POINTER_COMPRESSION, false));
241 g(gen_compare_ptr_tag(ctx, tmp_reg, tag, cond, label, tmp_reg));
244 g(gen_compare_ptr_tag(ctx, reg, tag, cond, label, tmp_reg));
248 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)
250 #if defined(REFCOUNT_TAG)
251 g(gen_compare_refcount(ctx, reg, tag, COND_NE, label));
253 g(gen_compare_ptr_tag(ctx, reg, tag, COND_NE, label, tmp_reg));
254 g(gen_compare_refcount(ctx, reg, REFCOUNT_STEP, COND_AE, label));
259 static bool attr_w gen_decompress_pointer(struct codegen_context *ctx, bool attr_unused zx, unsigned dest, unsigned src, int64_t offset)
261 #ifdef POINTER_COMPRESSION
262 #if defined(ARCH_X86) && POINTER_COMPRESSION <= 3
263 if (offset || dest != src) {
264 g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
265 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(ALU_ADD, is_imm()));
267 gen_one(ARG_SHIFTED_REGISTER);
268 gen_one(ARG_SHIFT_LSL | POINTER_COMPRESSION);
275 g(gen_extend(ctx, OP_SIZE_4, zero_x, dest, src));
278 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, dest, src, POINTER_COMPRESSION, false));
282 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, src, offset, 0));
286 static bool attr_w gen_compress_pointer(struct codegen_context attr_unused *ctx, unsigned dest, unsigned src)
288 #ifdef POINTER_COMPRESSION
289 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHR, dest, src, POINTER_COMPRESSION, false));
291 g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), dest, src));
296 static bool attr_w gen_frame_get_pointer(struct codegen_context *ctx, frame_t slot, bool deref, unsigned dest)
299 g(gen_upcall_start(ctx, 1));
300 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, R_ARG0));
301 g(gen_upcall_argument(ctx, 0));
302 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
303 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
304 } else if (!da(ctx->fn,function)->local_variables_flags[slot].may_be_borrowed) {
305 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
306 g(gen_set_1(ctx, R_FRAME, slot, 0, false));
307 flag_set(ctx, slot, false);
310 skip_label = alloc_label(ctx);
311 if (unlikely(!skip_label))
313 if (flag_is_set(ctx, slot)) {
314 g(gen_set_1(ctx, R_FRAME, slot, 0, false));
317 if (flag_is_clear(ctx, slot))
319 g(gen_test_1(ctx, R_FRAME, slot, 0, skip_label, false, TEST_CLEAR));
321 g(gen_upcall_start(ctx, 1));
322 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, R_ARG0));
323 g(gen_upcall_argument(ctx, 0));
324 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
326 gen_label(skip_label);
327 g(gen_frame_load(ctx, OP_SIZE_SLOT, garbage, slot, 0, dest));
328 g(gen_frame_clear(ctx, OP_SIZE_SLOT, slot));
329 flag_set(ctx, slot, false);
334 static bool attr_w gen_frame_set_pointer(struct codegen_context *ctx, frame_t slot, unsigned src, bool reference, bool not_thunk)
336 bool escape_on_thunk = !not_thunk && da(ctx->fn,function)->local_variables_flags[slot].must_be_data;
337 g(gen_set_1(ctx, R_FRAME, slot, 0, true));
338 flag_set_unknown(ctx, slot);
340 ctx->flag_cache[slot] |= FLAG_CACHE_IS_NOT_THUNK;
341 flag_set(ctx, slot, true);
342 g(gen_frame_store(ctx, OP_SIZE_SLOT, slot, 0, src));
345 g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_SAVED_1, src));
346 g(gen_upcall_start(ctx, 1));
347 g(gen_mov(ctx, i_size(OP_SIZE_ADDRESS), R_ARG0, src));
348 g(gen_upcall_argument(ctx, 0));
349 g(gen_upcall(ctx, offsetof(struct cg_upcall_vector_s, cg_upcall_pointer_reference_owned), 1));
353 if (escape_on_thunk) {
354 uint32_t nondata_label = alloc_escape_label_for_ip(ctx, ctx->current_position);
355 if (unlikely(!nondata_label))
357 g(gen_ptr_is_thunk(ctx, src, NO_FRAME_T, nondata_label));
362 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)
364 g(gen_ptr_is_thunk(ctx, src, NO_FRAME_T, escape_label));
366 if (flags & OPCODE_STRUCT_MAY_BORROW) {
367 g(gen_frame_store(ctx, OP_SIZE_SLOT, slot_r, 0, src));
368 flag_set(ctx, slot_r, false);
370 g(gen_frame_set_pointer(ctx, slot_r, src, true, false));
376 static bool attr_w gen_frame_load_slot(struct codegen_context *ctx, frame_t slot, unsigned reg)
378 g(gen_frame_load(ctx, OP_SIZE_SLOT, native, slot, 0, reg));
382 static bool attr_w gen_frame_get_slot(struct codegen_context *ctx, frame_t slot, unsigned reg, unsigned *dest)
384 if (ctx->registers[slot] >= 0) {
385 *dest = ctx->registers[slot];
389 g(gen_frame_load_slot(ctx, slot, reg));
393 static bool attr_w gen_frame_decompress_slot(struct codegen_context *ctx, frame_t slot, unsigned reg, unsigned *dest, uint32_t escape_label)
395 if (ctx->registers[slot] >= 0) {
396 unsigned creg = ctx->registers[slot];
397 g(gen_ptr_is_thunk(ctx, creg, slot, escape_label));
398 #if !defined(POINTER_COMPRESSION)
401 g(gen_decompress_pointer(ctx, ARCH_PREFERS_SX(OP_SIZE_SLOT), reg, creg, 0));
406 g(gen_frame_load(ctx, OP_SIZE_SLOT, native, slot, 0, reg));
407 g(gen_ptr_is_thunk(ctx, reg, slot, escape_label));
408 g(gen_decompress_pointer(ctx, ARCH_PREFERS_SX(OP_SIZE_SLOT), reg, reg, 0));