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[2];
93    coro_end_args[0] = coro_hdl;
94    coro_end_args[1] = LLVMConstInt(LLVMInt1TypeInContext(gallivm->context), 0, 0);
95    lp_build_intrinsic(gallivm->builder,
96                       "llvm.coro.end",
97                       LLVMInt1TypeInContext(gallivm->context),
98                       coro_end_args, 2, 0);
99 }
100 
lp_build_coro_resume(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)101 void lp_build_coro_resume(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
102 {
103    lp_build_intrinsic(gallivm->builder,
104                       "llvm.coro.resume",
105                       LLVMVoidTypeInContext(gallivm->context),
106                       &coro_hdl, 1, 0);
107 }
108 
lp_build_coro_destroy(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)109 void lp_build_coro_destroy(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
110 {
111    lp_build_intrinsic(gallivm->builder,
112                       "llvm.coro.destroy",
113                       LLVMVoidTypeInContext(gallivm->context),
114                       &coro_hdl, 1, 0);
115 }
116 
lp_build_coro_done(struct gallivm_state * gallivm,LLVMValueRef coro_hdl)117 LLVMValueRef lp_build_coro_done(struct gallivm_state *gallivm, LLVMValueRef coro_hdl)
118 {
119    return lp_build_intrinsic(gallivm->builder,
120                              "llvm.coro.done",
121                              LLVMInt1TypeInContext(gallivm->context),
122                              &coro_hdl, 1, 0);
123 }
124 
lp_build_coro_suspend(struct gallivm_state * gallivm,bool last)125 LLVMValueRef lp_build_coro_suspend(struct gallivm_state *gallivm, bool last)
126 {
127    LLVMValueRef coro_susp_args[2];
128    coro_susp_args[0] = LLVMConstNull(LLVMTokenTypeInContext(gallivm->context));
129    coro_susp_args[1] = LLVMConstInt(LLVMInt1TypeInContext(gallivm->context), last, 0);
130    LLVMValueRef coro_suspend = lp_build_intrinsic(gallivm->builder,
131                                                   "llvm.coro.suspend",
132                                                   LLVMInt8TypeInContext(gallivm->context),
133                                                   coro_susp_args, 2, 0);
134    return coro_suspend;
135 }
136 
lp_build_coro_alloc(struct gallivm_state * gallivm,LLVMValueRef id)137 LLVMValueRef lp_build_coro_alloc(struct gallivm_state *gallivm, LLVMValueRef id)
138 {
139    return lp_build_intrinsic(gallivm->builder,
140                              "llvm.coro.alloc",
141                              LLVMInt1TypeInContext(gallivm->context),
142                              &id, 1, 0);
143 }
144 
145 static char *
coro_malloc(int size)146 coro_malloc(int size)
147 {
148    return os_malloc_aligned(size, 4096);
149 }
150 
151 static void
coro_free(char * ptr)152 coro_free(char *ptr)
153 {
154    os_free_aligned(ptr);
155 }
156 
lp_build_coro_add_malloc_hooks(struct gallivm_state * gallivm)157 void lp_build_coro_add_malloc_hooks(struct gallivm_state *gallivm)
158 {
159    assert(gallivm->engine);
160 
161    assert(gallivm->coro_malloc_hook);
162    assert(gallivm->coro_free_hook);
163    LLVMAddGlobalMapping(gallivm->engine, gallivm->coro_malloc_hook, coro_malloc);
164    LLVMAddGlobalMapping(gallivm->engine, gallivm->coro_free_hook, coro_free);
165 }
166 
lp_build_coro_declare_malloc_hooks(struct gallivm_state * gallivm)167 void lp_build_coro_declare_malloc_hooks(struct gallivm_state *gallivm)
168 {
169    LLVMTypeRef int32_type = LLVMInt32TypeInContext(gallivm->context);
170    LLVMTypeRef mem_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0);
171    LLVMTypeRef malloc_type = LLVMFunctionType(mem_ptr_type, &int32_type, 1, 0);
172    gallivm->coro_malloc_hook = LLVMAddFunction(gallivm->module, "coro_malloc", malloc_type);
173    LLVMTypeRef free_type = LLVMFunctionType(LLVMVoidTypeInContext(gallivm->context), &mem_ptr_type, 1, 0);
174    gallivm->coro_free_hook = LLVMAddFunction(gallivm->module, "coro_free", free_type);
175 }
176 
lp_build_coro_begin_alloc_mem(struct gallivm_state * gallivm,LLVMValueRef coro_id)177 LLVMValueRef lp_build_coro_begin_alloc_mem(struct gallivm_state *gallivm, LLVMValueRef coro_id)
178 {
179    LLVMValueRef do_alloc = lp_build_coro_alloc(gallivm, coro_id);
180    LLVMTypeRef mem_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0);
181    LLVMValueRef alloc_mem_store = lp_build_alloca(gallivm, mem_ptr_type, "coro mem");
182    struct lp_build_if_state if_state_coro;
183    lp_build_if(&if_state_coro, gallivm, do_alloc);
184    LLVMValueRef coro_size = lp_build_coro_size(gallivm);
185    LLVMValueRef alloc_mem;
186 
187    assert(gallivm->coro_malloc_hook);
188    alloc_mem = LLVMBuildCall(gallivm->builder, gallivm->coro_malloc_hook, &coro_size, 1, "");
189 
190    LLVMBuildStore(gallivm->builder, alloc_mem, alloc_mem_store);
191    lp_build_endif(&if_state_coro);
192    alloc_mem = LLVMBuildLoad(gallivm->builder, alloc_mem_store, "");
193    LLVMValueRef coro_hdl = lp_build_coro_begin(gallivm, coro_id, alloc_mem);
194    return coro_hdl;
195 }
196 
lp_build_coro_free_mem(struct gallivm_state * gallivm,LLVMValueRef coro_id,LLVMValueRef coro_hdl)197 void lp_build_coro_free_mem(struct gallivm_state *gallivm, LLVMValueRef coro_id, LLVMValueRef coro_hdl)
198 {
199    LLVMValueRef alloc_mem = lp_build_coro_free(gallivm, coro_id, coro_hdl);
200 
201    assert(gallivm->coro_malloc_hook);
202    alloc_mem = LLVMBuildCall(gallivm->builder, gallivm->coro_free_hook, &alloc_mem, 1, "");
203 }
204 
lp_build_coro_suspend_switch(struct gallivm_state * gallivm,const struct lp_build_coro_suspend_info * sus_info,LLVMBasicBlockRef resume_block,bool final_suspend)205 void lp_build_coro_suspend_switch(struct gallivm_state *gallivm, const struct lp_build_coro_suspend_info *sus_info,
206                                   LLVMBasicBlockRef resume_block, bool final_suspend)
207 {
208    LLVMValueRef coro_suspend = lp_build_coro_suspend(gallivm, final_suspend);
209    LLVMValueRef myswitch = LLVMBuildSwitch(gallivm->builder, coro_suspend,
210                                            sus_info->suspend, resume_block ? 2 : 1);
211    LLVMAddCase(myswitch, LLVMConstInt(LLVMInt8TypeInContext(gallivm->context), 1, 0), sus_info->cleanup);
212    if (resume_block)
213       LLVMAddCase(myswitch, LLVMConstInt(LLVMInt8TypeInContext(gallivm->context), 0, 0), resume_block);
214 }
215