Itoyori  v0.0.1
aarch64.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "ityr/common/util.hpp"
4 
5 namespace ityr::ito {
6 
7 #if defined(__ARM_FEATURE_SVE)
8 #define ITYR_AARCH64_FLOAT_CLOBBERS \
9  "p0" , "p1" , "p2" , "p3" , "p4" , "p5" , "p6" , "p7" , \
10  "p8" , "p9" , "p10", "p11", "p12", "p13", "p14", "p15", \
11  "z0" , "z1" , "z2" , "z3" , "z4" , "z5" , "z6" , "z7" , \
12  "z8" , "z9" , "z10", "z11", "z12", "z13", "z14", "z15", \
13  "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", \
14  "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"
15 #else
16 #define ITYR_AARCH64_FLOAT_CLOBBERS \
17  "v0" , "v1" , "v2" , "v3" , "v4" , "v5" , "v6" , "v7" , \
18  "v8" , "v9" , "v10", "v11", "v12", "v13", "v14", "v15", \
19  "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", \
20  "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
21 #endif
22 
23 #if defined(__FUJITSU)
24 // Fujitsu compiler (trad mode of v1.2.31) generates the illegal instruction
25 // ldp x19, x19, [x29, #-16]
26 // if we specify x19 in the clobbered list.
27 // As a workaround, we save x19 in the stack explicitly
28 #define ITYR_AARCH64_SAVE_R19 "stp x0, x19, [sp, #-16]!\n\t"
29 #define ITYR_AARCH64_RESTORE_R19 "ldp x0, x19, [sp], #16\n\t"
30 #define ITYR_AARCH64_CLOBBER_R19
31 #else
32 #define ITYR_AARCH64_SAVE_R19
33 #define ITYR_AARCH64_RESTORE_R19
34 #define ITYR_AARCH64_CLOBBER_R19 "x19",
35 #endif
36 
37 // must be a callee-saved register
38 #define ITYR_AARCH64_ORIG_SP_REG "x20"
39 
41  void* fp;
42  void* lr;
43  void* saved_ptr;
45 };
46 
49  using save_context_fn_t = void (*)(context_frame*, void*, void*);
50  using call_on_stack_fn_t = void (*)(void*, void*, void*, void*);
51  using jump_to_stack_fn_t = void (*)(void*, void*, void*, void*);
52 
53 public:
54  static void save_context_with_call(context_frame* parent_cf,
55  save_context_fn_t fn,
56  void* arg0,
57  void* arg1,
58  void* saved_ptr = nullptr) {
59  register void* parent_cf_x9 asm("x9") = reinterpret_cast<void*>(parent_cf);
60  register void* fn_x10 asm("x10") = reinterpret_cast<void*>(fn);
61  register void* arg0_x1 asm("x1") = arg0;
62  register void* arg1_x2 asm("x2") = arg1;
63  register void* saved_ptr_x11 asm("x11") = saved_ptr;
64  asm volatile (
66  /* push saved_ptr and parent field of context */
67  "stp %4, %0, [sp, #-16]!\n\t"
68  /* save FP (r29) and LR (r30) */
69  "adr x30, 1f\n\t"
70  "stp x29, x30, [sp, #-16]!\n\t"
71  /* call function */
72  "mov x0, sp\n\t"
73  "blr %1\n\t"
74  /* skip saved FP and LR when normally returned */
75  "add sp, sp, #16\n\t"
76 
77  "1:\n\t"
78  /* skip saved_ptr and parent field */
79  "add sp, sp, #16\n\t"
81  : "+r"(parent_cf_x9), "+r"(fn_x10), "+r"(arg0_x1), "+r"(arg1_x2), "+r"(saved_ptr_x11)
82  :
83  : "x0", "x3", "x4", "x5", "x6", "x7",
84  "x8", "x12", "x13", "x14", "x15",
85  "x16", "x17", "x18", ITYR_AARCH64_CLOBBER_R19 "x20", "x21", "x22", "x23",
86  "x24", "x25", "x26", "x27", "x28", "x29", "x30",
88  "cc", "memory"
89  );
90  }
91 
92  static void resume(context_frame* cf) {
93  asm volatile (
94  "mov sp, %0\n\t"
95  "ldp x29, x30, [sp], #16\n\t"
96  "ret\n\t"
97  :
98  : "r"(cf)
99  :
100  );
101  // discard the current context
102  }
103 
104  static void call_on_stack(void* stack_buf,
105  size_t stack_size,
106  call_on_stack_fn_t fn,
107  void* arg0,
108  void* arg1,
109  void* arg2,
110  void* arg3) {
111  uintptr_t sp = reinterpret_cast<uintptr_t>(stack_buf) + stack_size - 1;
112  sp &= 0xFFFFFFFFFFFFFFF0;
113 
114  register void* sp_x9 asm("x9") = reinterpret_cast<void*>(sp);
115  register void* fn_x10 asm("x10") = reinterpret_cast<void*>(fn);
116  register void* arg0_x0 asm("x0") = arg0;
117  register void* arg1_x1 asm("x1") = arg1;
118  register void* arg2_x2 asm("x2") = arg2;
119  register void* arg3_x3 asm("x3") = arg3;
120  asm volatile (
121  "mov " ITYR_AARCH64_ORIG_SP_REG ", sp\n\t"
122  "mov sp, %0\n\t"
123  "blr %1\n\t"
124  "mov sp, " ITYR_AARCH64_ORIG_SP_REG "\n\t"
125  : "+r"(sp_x9), "+r"(fn_x10),
126  "+r"(arg0_x0), "+r"(arg1_x1), "+r"(arg2_x2), "+r"(arg3_x3)
127  :
128  : "x4", "x5", "x6", "x7",
129  "x8", "x11", "x12", "x13", "x14", "x15",
130  "x16", "x17", "x18", ITYR_AARCH64_ORIG_SP_REG,
131  /* callee-saved registers are saved */
133  "cc", "memory"
134  );
135  }
136 
137  static void jump_to_stack(void* stack_ptr,
138  jump_to_stack_fn_t fn,
139  void* arg0,
140  void* arg1,
141  void* arg2,
142  void* arg3) {
143  uintptr_t sp = reinterpret_cast<uintptr_t>(stack_ptr) & 0xFFFFFFFFFFFFFFF0;
144 
145  register void* arg0_x0 asm("x0") = arg0;
146  register void* arg1_x1 asm("x1") = arg1;
147  register void* arg2_x2 asm("x2") = arg2;
148  register void* arg3_x3 asm("x3") = arg3;
149  asm volatile (
150  "mov sp, %0\n\t"
151  "blr %1\n\t"
152  :
153  : "r"(sp), "r"(fn),
154  "r"(arg0_x0), "r"(arg1_x1), "r"(arg2_x2), "r"(arg3_x3)
155  :
156  );
157  // discard the current context
158  }
159 
161  // Workaround for generating backtracing.
162  // Backtracing in libunwind often causes segfault because of the stack management.
163  // That is, because stacks are moved across different nodes, their parent stack may not exist
164  // in the current node. Thus, we clear the frame pointer and instruction pointer outside the
165  // current stack (which should be in the parent stack area), so that backtracing does not
166  // go further than that.
167  if (cf->parent_frame) {
168  cf->parent_frame->fp = nullptr;
169  cf->parent_frame->lr = nullptr;
170  }
171  }
172 
173 };
174 
175 }
176 
#define ITYR_AARCH64_FLOAT_CLOBBERS
Definition: aarch64.hpp:16
#define ITYR_AARCH64_ORIG_SP_REG
Definition: aarch64.hpp:38
#define ITYR_AARCH64_RESTORE_R19
Definition: aarch64.hpp:33
#define ITYR_AARCH64_SAVE_R19
Definition: aarch64.hpp:32
#define ITYR_AARCH64_CLOBBER_R19
Definition: aarch64.hpp:34
Definition: aarch64.hpp:47
static void call_on_stack(void *stack_buf, size_t stack_size, call_on_stack_fn_t fn, void *arg0, void *arg1, void *arg2, void *arg3)
Definition: aarch64.hpp:104
static void resume(context_frame *cf)
Definition: aarch64.hpp:92
static void jump_to_stack(void *stack_ptr, jump_to_stack_fn_t fn, void *arg0, void *arg1, void *arg2, void *arg3)
Definition: aarch64.hpp:137
static void clear_parent_frame(context_frame *cf)
Definition: aarch64.hpp:160
static void save_context_with_call(context_frame *parent_cf, save_context_fn_t fn, void *arg0, void *arg1, void *saved_ptr=nullptr)
Definition: aarch64.hpp:54
Definition: aarch64.hpp:5
Definition: aarch64.hpp:40
void * saved_ptr
Definition: aarch64.hpp:43
void * lr
Definition: aarch64.hpp:42
void * fp
Definition: aarch64.hpp:41
context_frame_aarch64 * parent_frame
Definition: aarch64.hpp:44