Itoyori  v0.0.1
x86_64.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(__AVX512F__)
8 #define ITYR_X86_64_FLOAT_REGS \
9  "%xmm0" , "%xmm1" , "%xmm2" , "%xmm3" , "%xmm4" , "%xmm5" , "%xmm6" , "%xmm7" , \
10  "%xmm8" , "%xmm9" , "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15", \
11  "%xmm16", "%xmm17", "%xmm18", "%xmm19", "%xmm20", "%xmm21", "%xmm22", "%xmm23", \
12  "%xmm24", "%xmm25", "%xmm26", "%xmm27", "%xmm28", "%xmm29", "%xmm30", "%xmm31"
13 #else
14 #define ITYR_X86_64_FLOAT_REGS \
15  "%xmm0" , "%xmm1" , "%xmm2" , "%xmm3" , "%xmm4" , "%xmm5" , "%xmm6" , "%xmm7" , \
16  "%xmm8" , "%xmm9" , "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15"
17 #endif
18 
20  void* rip;
21  void* rsp;
22  void* rbp;
23  void* saved_ptr;
25 };
26 
29  using save_context_fn_t = void (*)(context_frame*, void*, void*);
30  using call_on_stack_fn_t = void (*)(void*, void*, void*, void*);
31  using jump_to_stack_fn_t = void (*)(void*, void*, void*, void*);
32 
33 public:
34  static void save_context_with_call(context_frame* parent_cf,
35  save_context_fn_t fn,
36  void* arg0,
37  void* arg1,
38  void* saved_ptr = nullptr) {
39  register void* parent_cf_r8 asm("r8") = reinterpret_cast<void*>(parent_cf);
40  register void* fn_r9 asm("r9") = reinterpret_cast<void*>(fn);
41  register void* saved_ptr_r10 asm("r10") = reinterpret_cast<void*>(saved_ptr);
42  asm volatile (
43  /* save red zone */
44  "sub $128, %%rsp\n\t"
45  /* 16-byte sp alignment for SIMD registers */
46  "mov %%rsp, %%rax\n\t"
47  "and $0xFFFFFFFFFFFFFFF0, %%rsp\n\t"
48  "push %%rax\n\t"
49  /* parent field of context frame */
50  "push %0\n\t"
51  /* push saved_ptr */
52  "push %4\n\t"
53  /* push rbp */
54  "push %%rbp\n\t"
55  /* sp */
56  "lea -16(%%rsp), %%rax\n\t"
57  "push %%rax\n\t"
58  /* ip */
59  "lea 1f(%%rip), %%rax\n\t"
60  "push %%rax\n\t"
61  /* call function */
62  "mov %%rsp, %%rdi\n\t"
63  "call *%1\n\t"
64  /* pop ip from stack */
65  "add $8, %%rsp\n\t"
66 
67  "1:\n\t" /* ip is popped with ret operation at resume */
68  /* pop sp */
69  "add $8, %%rsp\n\t"
70  /* pop rbp */
71  "pop %%rbp\n\t"
72  /* saved_ptr and parent field of context frame */
73  "add $16, %%rsp\n\t"
74  /* revert sp alignmment */
75  "pop %%rsp\n\t"
76  /* restore red zone */
77  "add $128, %%rsp\n\t"
78  : "+r"(parent_cf_r8), "+r"(fn_r9),
79  "+S"(arg0), "+d"(arg1), "+r"(saved_ptr_r10)
80  :
81  : "%rax", "%rbx", "%rcx", "%rdi",
82  "%r11", "%r12", "%r13", "%r14", "%r15",
84  "cc", "memory"
85  );
86  }
87 
88  static void resume(context_frame* cf) {
89  asm volatile (
90  "mov %0, %%rsp\n\t"
91  "ret\n\t"
92  :
93  : "g"(cf)
94  :
95  );
96  // discard the current context
97  }
98 
99  static void call_on_stack(void* stack_buf,
100  size_t stack_size,
101  call_on_stack_fn_t fn,
102  void* arg0,
103  void* arg1,
104  void* arg2,
105  void* arg3) {
106  uintptr_t sp = reinterpret_cast<uintptr_t>(stack_buf) + stack_size - 1;
107  sp &= 0xFFFFFFFFFFFFFFF0;
108 
109  register void* sp_r8 asm("r8") = reinterpret_cast<void*>(sp);
110  register void* fn_r9 asm("r9") = reinterpret_cast<void*>(fn);
111  asm volatile (
112  "mov %%rsp, %%rax\n\t"
113  "mov %0, %%rsp\n\t"
114  /* alignment for SIMD register accesses */
115  "sub $0x8, %%rsp\n\t"
116  "push %%rax\n\t"
117  "call *%1\n\t"
118  "pop %%rsp\n\t"
119  : "+r"(sp_r8), "+r"(fn_r9),
120  "+D"(arg0), "+S"(arg1), "+d"(arg2), "+c"(arg3)
121  :
122  : "%rax", "%rbx",
123  "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
125  "cc", "memory"
126  );
127  }
128 
129  static void jump_to_stack(void* stack_ptr,
130  jump_to_stack_fn_t fn,
131  void* arg0,
132  void* arg1,
133  void* arg2,
134  void* arg3) {
135  uintptr_t sp = reinterpret_cast<uintptr_t>(stack_ptr) & 0xFFFFFFFFFFFFFFF0;
136 
137  asm volatile (
138  "mov %0, %%rsp\n\t"
139  "call *%1\n\t"
140  :
141  : "g"(sp), "r"(fn),
142  "D"(arg0), "S"(arg1), "d"(arg2), "c"(arg3)
143  :
144  );
145  // discard the current context
146  }
147 
149  // Workaround for generating backtracing.
150  // Backtracing in libunwind often causes segfault because of the stack management.
151  // That is, because stacks are moved across different nodes, their parent stack may not exist
152  // in the current node. Thus, we clear the frame pointer and instruction pointer outside the
153  // current stack (which should be in the parent stack area), so that backtracing does not
154  // go further than that.
155  if (cf->parent_frame) {
156  cf->parent_frame->rip = nullptr;
157  cf->parent_frame->rsp = nullptr;
158  cf->parent_frame->rbp = nullptr;
159  }
160  }
161 
162 };
163 
164 }
Definition: x86_64.hpp:27
static void jump_to_stack(void *stack_ptr, jump_to_stack_fn_t fn, void *arg0, void *arg1, void *arg2, void *arg3)
Definition: x86_64.hpp:129
static void resume(context_frame *cf)
Definition: x86_64.hpp:88
static void clear_parent_frame(context_frame *cf)
Definition: x86_64.hpp:148
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: x86_64.hpp:99
static void save_context_with_call(context_frame *parent_cf, save_context_fn_t fn, void *arg0, void *arg1, void *saved_ptr=nullptr)
Definition: x86_64.hpp:34
Definition: aarch64.hpp:5
Definition: x86_64.hpp:19
void * rip
Definition: x86_64.hpp:20
void * rsp
Definition: x86_64.hpp:21
context_frame_x86_64 * parent_frame
Definition: x86_64.hpp:24
void * rbp
Definition: x86_64.hpp:22
void * saved_ptr
Definition: x86_64.hpp:23
#define ITYR_X86_64_FLOAT_REGS
Definition: x86_64.hpp:14