1 /**************************************************************************
2 *
3 * Copyright 2019 Red Hat.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 **************************************************************************/
25
26 #include <stdint.h>
27 #include "lp_bld_coro.h"
28 #include "util/os_memory.h"
29 #include "lp_bld_init.h"
30 #include "lp_bld_const.h"
31 #include "lp_bld_intr.h"
32 #include "lp_bld_flow.h"
33
34 #if LLVM_VERSION_MAJOR < 6
35 /* not a wrapper, just lets it compile */
LLVMTokenTypeInContext(LLVMContextRef C)36 static LLVMTypeRef LLVMTokenTypeInContext(LLVMContextRef C)
37 {
38 assert(0);
39 return LLVMVoidTypeInContext(C);
40 }
41 #endif
42
lp_build_coro_id(struct gallivm_state * gallivm)43 LLVMValueRef lp_build_coro_id(struct gallivm_state *gallivm)
44 {
45 LLVMValueRef coro_id_args[4];
46 coro_id_args[0] = lp_build_const_int32(gallivm, 0);
47 coro_id_args[1] = LLVMConstPointerNull(LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0));
48 coro_id_args[2] = coro_id_args[1];
49 coro_id_args[3] = coro_id_args[1];
50 LLVMValueRef coro_id = lp_build_intrinsic(gallivm->builder,
51 "llvm.coro.id",
52 LLVMTokenTypeInContext(gallivm->context),
53 coro_id_args, 4, 0);
54 return coro_id;
55 }
56
lp_build_coro_size(struct gallivm_state * gallivm)57 LLVMValueRef lp_build_coro_size(struct gallivm_state *gallivm)
58 {
59 return lp_build_intrinsic(gallivm->builder,
60 "llvm.coro.size.i32",
61 LLVMInt32TypeInContext(gallivm->context),
62 NULL, 0, 0);
63 }
64
lp_build_coro_begin(struct gallivm_state * gallivm,LLVMValueRef coro_id,LLVMValueRef mem_ptr)65 LLVMValueRef lp_build_coro_begin(struct gallivm_state *gallivm,
66 LLVMValueRef coro_id, LLVMValueRef mem_ptr)
67 {
68 LLVMValueRef coro_begin_args[2];
69 coro_begin_args[0] = coro_id;
70 coro_begin_args[1] = mem_ptr;
71 LLVMValueRef coro_hdl = lp_build_intrinsic(gallivm->builder,
72 "llvm.coro.begin",
73 LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0),
74 coro_begin_args, 2, 0);
75 return coro_hdl;
76 }
77
lp_build_coro_free(struct gallivm_state * gallivm,LLVMValueRef coro_id,LLVMValueRef coro_hdl)78 LLVMValueRef lp_build_coro_free(struct gallivm_state *gallivm,
79 LLVMValueRef coro_id, LLVMValueRef coro_hdl)
80 {
81 LLVMValueRef coro_free_args[2];
82 coro_free_args[0] = coro_id;
83 coro_free_args[1] = coro_hdl;
84 return lp_build_intrinsic(gallivm->builder,
85 "llvm.coro.free",
86 LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0),
87 coro_free_args, 2, 0);
88 }
89
lp_build_coro_end(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)90 void lp_build_coro_end(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
91 {
92 LLVMValueRef coro_end_args[3];
93 int num_args = 2;
94 coro_end_args[0] = coro_hdl;
95 coro_end_args[1] = LLVMConstInt(LLVMInt1TypeInContext(gallivm->context), 0, 0);
96 #if LLVM_VERSION_MAJOR >= 18
97 coro_end_args[2] = LLVMConstNull(LLVMTokenTypeInContext(gallivm->context));
98 num_args++;
99 #endif
100 lp_build_intrinsic(gallivm->builder,
101 "llvm.coro.end",
102 LLVMInt1TypeInContext(gallivm->context),
103 coro_end_args, num_args, 0);
104 }
105
lp_build_coro_resume(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)106 void lp_build_coro_resume(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
107 {
108 lp_build_intrinsic(gallivm->builder,
109 "llvm.coro.resume",
110 LLVMVoidTypeInContext(gallivm->context),
111 &coro_hdl, 1, 0);
112 }
113
lp_build_coro_destroy(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)114 void lp_build_coro_destroy(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
115 {
116 lp_build_intrinsic(gallivm->builder,
117 "llvm.coro.destroy",
118 LLVMVoidTypeInContext(gallivm->context),
119 &coro_hdl, 1, 0);
120 }
121
lp_build_coro_done(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)122 LLVMValueRef lp_build_coro_done(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
123 {
124 return lp_build_intrinsic(gallivm->builder,
125 "llvm.coro.done",
126 LLVMInt1TypeInContext(gallivm->context),
127 &coro_hdl, 1, 0);
128 }
129
lp_build_coro_suspend(struct gallivm_state * gallivm,bool last)130 LLVMValueRef lp_build_coro_suspend(struct gallivm_state *gallivm, bool last)
131 {
132 LLVMValueRef coro_susp_args[2];
133 coro_susp_args[0] = LLVMConstNull(LLVMTokenTypeInContext(gallivm->context));
134 coro_susp_args[1] = LLVMConstInt(LLVMInt1TypeInContext(gallivm->context), last, 0);
135 LLVMValueRef coro_suspend = lp_build_intrinsic(gallivm->builder,
136 "llvm.coro.suspend",
137 LLVMInt8TypeInContext(gallivm->context),
138 coro_susp_args, 2, 0);
139 return coro_suspend;
140 }
141
lp_build_coro_alloc(struct gallivm_state * gallivm,LLVMValueRef id)142 LLVMValueRef lp_build_coro_alloc(struct gallivm_state *gallivm, LLVMValueRef id)
143 {
144 return lp_build_intrinsic(gallivm->builder,
145 "llvm.coro.alloc",
146 LLVMInt1TypeInContext(gallivm->context),
147 &id, 1, 0);
148 }
149
150 static char *
coro_malloc(int size)151 coro_malloc(int size)
152 {
153 return os_malloc_aligned(size, 4096);
154 }
155
156 static void
coro_free(char * ptr)157 coro_free(char *ptr)
158 {
159 os_free_aligned(ptr);
160 }
161
lp_build_coro_add_malloc_hooks(struct gallivm_state * gallivm)162 void lp_build_coro_add_malloc_hooks(struct gallivm_state *gallivm)
163 {
164 #if !GALLIVM_USE_ORCJIT
165 assert(gallivm->engine);
166 #endif
167
168 assert(gallivm->coro_malloc_hook);
169 assert(gallivm->coro_free_hook);
170 gallivm_add_global_mapping(gallivm, gallivm->coro_malloc_hook, coro_malloc);
171 gallivm_add_global_mapping(gallivm, gallivm->coro_free_hook, coro_free);
172 }
173
lp_build_coro_declare_malloc_hooks(struct gallivm_state * gallivm)174 void lp_build_coro_declare_malloc_hooks(struct gallivm_state *gallivm)
175 {
176 LLVMTypeRef int32_type = LLVMInt32TypeInContext(gallivm->context);
177 LLVMTypeRef mem_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0);
178 LLVMTypeRef malloc_type = LLVMFunctionType(mem_ptr_type, &int32_type, 1, 0);
179 gallivm->coro_malloc_hook_type = malloc_type;
180 gallivm->coro_malloc_hook = LLVMAddFunction(gallivm->module, "coro_malloc", malloc_type);
181 LLVMTypeRef free_type = LLVMFunctionType(LLVMVoidTypeInContext(gallivm->context), &mem_ptr_type, 1, 0);
182 gallivm->coro_free_hook_type = free_type;
183 gallivm->coro_free_hook = LLVMAddFunction(gallivm->module, "coro_free", free_type);
184 }
185
lp_build_coro_begin_alloc_mem(struct gallivm_state * gallivm,LLVMValueRef coro_id)186 LLVMValueRef lp_build_coro_begin_alloc_mem(struct gallivm_state *gallivm, LLVMValueRef coro_id)
187 {
188 LLVMTypeRef mem_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0);
189 LLVMValueRef do_alloc = lp_build_coro_alloc(gallivm, coro_id);
190 struct lp_build_if_state if_state_coro;
191 lp_build_if(&if_state_coro, gallivm, do_alloc);
192 LLVMValueRef coro_size = lp_build_coro_size(gallivm);
193 LLVMValueRef alloc_mem;
194
195 assert(gallivm->coro_malloc_hook);
196 LLVMTypeRef malloc_type =
197 LLVMFunctionType(mem_ptr_type,
198 (LLVMTypeRef[]){LLVMInt32TypeInContext(gallivm->context)}, 1, 0);
199 alloc_mem = LLVMBuildCall2(gallivm->builder, malloc_type, gallivm->coro_malloc_hook, &coro_size, 1, "");
200 lp_build_endif(&if_state_coro);
201
202 LLVMValueRef phi = LLVMBuildPhi(gallivm->builder, mem_ptr_type, "");
203 LLVMValueRef zero_bool = LLVMConstNull(mem_ptr_type);
204 LLVMAddIncoming(phi, &alloc_mem, &if_state_coro.true_block, 1);
205 LLVMAddIncoming(phi, &zero_bool, &if_state_coro.entry_block, 1);
206
207 LLVMValueRef coro_hdl = lp_build_coro_begin(gallivm, coro_id, phi);
208 return coro_hdl;
209 }
210
lp_build_coro_alloc_mem_array(struct gallivm_state * gallivm,LLVMValueRef coro_hdl_ptr,LLVMValueRef coro_idx,LLVMValueRef coro_num_hdls)211 LLVMValueRef lp_build_coro_alloc_mem_array(struct gallivm_state *gallivm,
212 LLVMValueRef coro_hdl_ptr, LLVMValueRef coro_idx,
213 LLVMValueRef coro_num_hdls)
214 {
215 LLVMTypeRef mem_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0);
216 LLVMValueRef alloced_ptr = LLVMBuildLoad2(gallivm->builder, mem_ptr_type, coro_hdl_ptr, "");
217
218 LLVMValueRef not_alloced = LLVMBuildICmp(gallivm->builder, LLVMIntEQ, alloced_ptr, LLVMConstNull(mem_ptr_type), "");
219 LLVMValueRef coro_size = lp_build_coro_size(gallivm);
220
221 struct lp_build_if_state if_state_coro;
222 lp_build_if(&if_state_coro, gallivm, not_alloced);
223
224 LLVMValueRef alloc_mem;
225 LLVMValueRef alloc_size = LLVMBuildMul(gallivm->builder, coro_num_hdls, coro_size, "");
226 assert(gallivm->coro_malloc_hook);
227 assert(gallivm->coro_malloc_hook_type);
228 alloc_mem = LLVMBuildCall2(gallivm->builder, gallivm->coro_malloc_hook_type, gallivm->coro_malloc_hook, &alloc_size, 1, "");
229 LLVMBuildStore(gallivm->builder, alloc_mem, coro_hdl_ptr);
230 lp_build_endif(&if_state_coro);
231
232 return LLVMBuildMul(gallivm->builder, coro_size, coro_idx, "");
233 }
234
lp_build_coro_free_mem(struct gallivm_state * gallivm,LLVMValueRef coro_id,LLVMValueRef coro_hdl)235 void lp_build_coro_free_mem(struct gallivm_state *gallivm, LLVMValueRef coro_id, LLVMValueRef coro_hdl)
236 {
237 LLVMValueRef alloc_mem = lp_build_coro_free(gallivm, coro_id, coro_hdl);
238
239 assert(gallivm->coro_free_hook);
240 assert(gallivm->coro_free_hook_type);
241 alloc_mem = LLVMBuildCall2(gallivm->builder, gallivm->coro_free_hook_type, gallivm->coro_free_hook, &alloc_mem, 1, "");
242 }
243
lp_build_coro_suspend_switch(struct gallivm_state * gallivm,const struct lp_build_coro_suspend_info * sus_info,LLVMBasicBlockRef resume_block,bool final_suspend)244 void lp_build_coro_suspend_switch(struct gallivm_state *gallivm, const struct lp_build_coro_suspend_info *sus_info,
245 LLVMBasicBlockRef resume_block, bool final_suspend)
246 {
247 LLVMValueRef coro_suspend = lp_build_coro_suspend(gallivm, final_suspend);
248 LLVMValueRef myswitch = LLVMBuildSwitch(gallivm->builder, coro_suspend,
249 sus_info->suspend, resume_block ? 2 : 1);
250 LLVMAddCase(myswitch, LLVMConstInt(LLVMInt8TypeInContext(gallivm->context), 1, 0), sus_info->cleanup);
251 if (resume_block)
252 LLVMAddCase(myswitch, LLVMConstInt(LLVMInt8TypeInContext(gallivm->context), 0, 0), resume_block);
253 }
254