Mercurial > projects > ldc
comparison gen/tocall.cpp @ 414:ac1fcc138e42
Fixed issue with internal real representation, incorrect for non x86-32 architectures.
Cleaned up CallExp::toElem, moved implementation to tocall.cpp providing a single procedure to call arbitrary D functions fairly easily.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Mon, 28 Jul 2008 02:11:34 +0200 |
parents | 6057fdf797d8 |
children | fa91b03d9cd7 |
comparison
equal
deleted
inserted
replaced
413:1a9bdbd4ac60 | 414:ac1fcc138e42 |
---|---|
11 | 11 |
12 #include "gen/logger.h" | 12 #include "gen/logger.h" |
13 | 13 |
14 ////////////////////////////////////////////////////////////////////////////////////////// | 14 ////////////////////////////////////////////////////////////////////////////////////////// |
15 | 15 |
16 DValue* DtoCallDFunc(FuncDeclaration* fdecl, Array* arguments, TypeClass* type, LLValue* thismem) | 16 TypeFunction* DtoTypeFunction(Type* type) |
17 { | 17 { |
18 Logger::println("Calling function: %s", fdecl->toPrettyChars()); | 18 TypeFunction* tf = 0; |
19 LOG_SCOPE; | 19 type = type->toBasetype(); |
20 | 20 if (type->ty == Tfunction) |
21 assert(fdecl); | 21 { |
22 DtoForceDeclareDsymbol(fdecl); | 22 tf = (TypeFunction*)type; |
23 llvm::Function* fn = fdecl->ir.irFunc->func; | 23 } |
24 TypeFunction* tf = (TypeFunction*)DtoDType(fdecl->type); | 24 else if (type->ty == Tdelegate) |
25 | 25 { |
26 assert(type->next->ty == Tfunction); | |
27 tf = (TypeFunction*)type->next; | |
28 } | |
29 return tf; | |
30 } | |
31 | |
32 ////////////////////////////////////////////////////////////////////////////////////////// | |
33 | |
34 unsigned DtoCallingConv(LINK l) | |
35 { | |
36 if (l == LINKc || l == LINKcpp) | |
37 return llvm::CallingConv::C; | |
38 else if (l == LINKd || l == LINKdefault) | |
39 return llvm::CallingConv::Fast; | |
40 else if (l == LINKwindows) | |
41 return llvm::CallingConv::X86_StdCall; | |
42 else | |
43 assert(0 && "Unsupported calling convention"); | |
44 } | |
45 | |
46 ////////////////////////////////////////////////////////////////////////////////////////// | |
47 | |
48 DValue* DtoVaArg(Loc& loc, Type* type, Expression* valistArg) | |
49 { | |
50 DValue* expelem = valistArg->toElem(gIR); | |
51 const LLType* llt = DtoType(type); | |
52 if (DtoIsPassedByRef(type)) | |
53 llt = getPtrToType(llt); | |
54 // issue a warning for broken va_arg instruction. | |
55 if (global.params.cpu != ARCHx86) | |
56 warning("%s: va_arg for C variadic functions is probably broken for anything but x86", loc.toChars()); | |
57 // done | |
58 return new DImValue(type, gIR->ir->CreateVAArg(expelem->getLVal(), llt, "tmp")); | |
59 } | |
60 | |
61 ////////////////////////////////////////////////////////////////////////////////////////// | |
62 | |
63 LLValue* DtoCallableValue(DValue* fn) | |
64 { | |
65 Type* type = fn->getType()->toBasetype(); | |
66 if (type->ty == Tfunction) | |
67 { | |
68 return fn->getRVal(); | |
69 } | |
70 else if (type->ty == Tdelegate) | |
71 { | |
72 LLValue* dg = fn->getRVal(); | |
73 LLValue* funcptr = DtoGEPi(dg, 0, 1); | |
74 return DtoLoad(funcptr); | |
75 } | |
76 else | |
77 { | |
78 assert(0 && "not a callable type"); | |
79 return NULL; | |
80 } | |
81 } | |
82 | |
83 ////////////////////////////////////////////////////////////////////////////////////////// | |
84 | |
85 const LLFunctionType* DtoExtractFunctionType(const LLType* type) | |
86 { | |
87 if (const LLFunctionType* fty = isaFunction(type)) | |
88 return fty; | |
89 else if (const LLPointerType* pty = isaPointer(type)) | |
90 { | |
91 if (const LLFunctionType* fty = isaFunction(pty->getElementType())) | |
92 return fty; | |
93 } | |
94 return NULL; | |
95 } | |
96 | |
97 ////////////////////////////////////////////////////////////////////////////////////////// | |
98 | |
99 void DtoBuildDVarArgList(std::vector<LLValue*>& args, llvm::PAListPtr& palist, TypeFunction* tf, Expressions* arguments, size_t argidx) | |
100 { | |
101 Logger::println("doing d-style variadic arguments"); | |
102 | |
103 std::vector<const LLType*> vtypes; | |
104 | |
105 // number of non variadic args | |
106 int begin = tf->parameters->dim; | |
107 Logger::println("num non vararg params = %d", begin); | |
108 | |
109 // build struct with argument types (non variadic args) | |
110 for (int i=begin; i<arguments->dim; i++) | |
111 { | |
112 Expression* argexp = (Expression*)arguments->data[i]; | |
113 vtypes.push_back(DtoType(argexp->type)); | |
114 size_t sz = getABITypeSize(vtypes.back()); | |
115 if (sz < PTRSIZE) | |
116 vtypes.back() = DtoSize_t(); | |
117 } | |
118 const LLStructType* vtype = LLStructType::get(vtypes); | |
119 Logger::cout() << "d-variadic argument struct type:\n" << *vtype << '\n'; | |
120 LLValue* mem = new llvm::AllocaInst(vtype,"_argptr_storage",gIR->topallocapoint()); | |
121 | |
122 // store arguments in the struct | |
123 for (int i=begin,k=0; i<arguments->dim; i++,k++) | |
124 { | |
125 Expression* argexp = (Expression*)arguments->data[i]; | |
126 if (global.params.llvmAnnotate) | |
127 DtoAnnotation(argexp->toChars()); | |
128 LLValue* argdst = DtoGEPi(mem,0,k); | |
129 argdst = DtoBitCast(argdst, getPtrToType(DtoType(argexp->type))); | |
130 DtoVariadicArgument(argexp, argdst); | |
131 } | |
132 | |
133 // build type info array | |
134 assert(Type::typeinfo->ir.irStruct->constInit); | |
135 const LLType* typeinfotype = DtoType(Type::typeinfo->type); | |
136 const LLArrayType* typeinfoarraytype = LLArrayType::get(typeinfotype,vtype->getNumElements()); | |
137 | |
138 llvm::GlobalVariable* typeinfomem = | |
139 new llvm::GlobalVariable(typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, NULL, "._arguments.storage", gIR->module); | |
140 Logger::cout() << "_arguments storage: " << *typeinfomem << '\n'; | |
141 | |
142 std::vector<LLConstant*> vtypeinfos; | |
143 for (int i=begin,k=0; i<arguments->dim; i++,k++) | |
144 { | |
145 Expression* argexp = (Expression*)arguments->data[i]; | |
146 vtypeinfos.push_back(DtoTypeInfoOf(argexp->type)); | |
147 } | |
148 | |
149 // apply initializer | |
150 LLConstant* tiinits = llvm::ConstantArray::get(typeinfoarraytype, vtypeinfos); | |
151 typeinfomem->setInitializer(tiinits); | |
152 | |
153 // put data in d-array | |
154 std::vector<LLConstant*> pinits; | |
155 pinits.push_back(DtoConstSize_t(vtype->getNumElements())); | |
156 pinits.push_back(llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype))); | |
157 const LLType* tiarrty = DtoType(Type::typeinfo->type->arrayOf()); | |
158 tiinits = llvm::ConstantStruct::get(pinits); | |
159 LLValue* typeinfoarrayparam = new llvm::GlobalVariable(tiarrty, | |
160 true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array", gIR->module); | |
161 | |
162 // specify arguments | |
163 args.push_back(typeinfoarrayparam); | |
164 ++argidx; | |
165 args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::Int8Ty), "tmp")); | |
166 ++argidx; | |
167 | |
168 // pass non variadic args | |
169 for (int i=0; i<begin; i++) | |
170 { | |
171 Argument* fnarg = Argument::getNth(tf->parameters, i); | |
172 DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); | |
173 args.push_back(argval->getRVal()); | |
174 | |
175 if (fnarg->llvmByVal) | |
176 palist = palist.addAttr(argidx, llvm::ParamAttr::ByVal); | |
177 | |
178 ++argidx; | |
179 } | |
180 } | |
181 | |
182 | |
183 DValue* DtoCallFunction(Type* resulttype, DValue* fnval, Expressions* arguments) | |
184 { | |
185 // the callee D type | |
186 Type* calleeType = fnval->getType(); | |
187 | |
188 // get func value if any | |
189 DFuncValue* dfnval = fnval->isFunc(); | |
190 | |
191 // handle special va_copy / va_end intrinsics | |
192 bool va_intrinsic = (dfnval && dfnval->func && (dfnval->func->llvmInternal == LLVMva_intrinsic)); | |
193 | |
194 // get function type info | |
195 TypeFunction* tf = DtoTypeFunction(calleeType); | |
196 assert(tf); | |
197 | |
198 // misc | |
199 bool retinptr = tf->llvmRetInPtr; | |
200 bool usesthis = tf->llvmUsesThis; | |
201 bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate); | |
202 bool nestedcall = (dfnval && dfnval->func && dfnval->func->isNested()); | |
203 bool dvarargs = (tf->linkage == LINKd && tf->varargs == 1); | |
204 | |
205 unsigned callconv = DtoCallingConv(tf->linkage); | |
206 | |
207 // get callee llvm value | |
208 LLValue* callable = DtoCallableValue(fnval); | |
209 const LLFunctionType* callableTy = DtoExtractFunctionType(callable->getType()); | |
210 assert(callableTy); | |
211 | |
212 // get llvm argument iterator, for types | |
213 LLFunctionType::param_iterator argbegin = callableTy->param_begin(); | |
214 LLFunctionType::param_iterator argiter = argbegin; | |
215 | |
216 // handle implicit arguments | |
217 std::vector<LLValue*> args; | |
218 | |
219 // return in hidden ptr is first | |
220 if (retinptr) | |
221 { | |
222 LLValue* retvar = new llvm::AllocaInst(argiter->get()->getContainedType(0), ".rettmp", gIR->topallocapoint()); | |
223 ++argiter; | |
224 args.push_back(retvar); | |
225 } | |
226 | |
227 // then comes the 'this' argument | |
228 if (dfnval && dfnval->vthis) | |
229 { | |
230 LLValue* thisarg = DtoBitCast(dfnval->vthis, argiter->get()); | |
231 ++argiter; | |
232 args.push_back(thisarg); | |
233 } | |
234 // or a delegate context arg | |
235 else if (delegatecall) | |
236 { | |
237 LLValue* ctxarg = DtoLoad(DtoGEPi(fnval->getRVal(), 0,0)); | |
238 assert(ctxarg->getType() == argiter->get()); | |
239 ++argiter; | |
240 args.push_back(ctxarg); | |
241 } | |
242 // or a nested function context arg | |
243 else if (nestedcall) | |
244 { | |
245 LLValue* contextptr = DtoNestedContext(dfnval->func->toParent2()->isFuncDeclaration()); | |
246 if (!contextptr) | |
247 contextptr = getNullPtr(getVoidPtrType()); | |
248 else | |
249 contextptr = DtoBitCast(contextptr, getVoidPtrType()); | |
250 ++argiter; | |
251 args.push_back(contextptr); | |
252 } | |
253 | |
254 // handle the rest of the arguments based on param passing style | |
26 llvm::PAListPtr palist; | 255 llvm::PAListPtr palist; |
27 | 256 |
28 int thisOffset = 0; | 257 // variadic instrinsics need some custom casts |
29 if (type || thismem) | 258 if (va_intrinsic) |
30 { | 259 { |
31 assert(type && thismem); | 260 size_t n = arguments->dim; |
32 thisOffset = 1; | 261 for (int i=0; i<n; i++) |
33 } | |
34 | |
35 std::vector<LLValue*> args; | |
36 if (thisOffset) | |
37 args.push_back(thismem); | |
38 for (size_t i=0; i<arguments->dim; ++i) | |
39 { | |
40 Expression* ex = (Expression*)arguments->data[i]; | |
41 Argument* fnarg = Argument::getNth(tf->parameters, i); | |
42 DValue* argval = DtoArgument(fnarg, ex); | |
43 LLValue* a = argval->getRVal(); | |
44 const LLType* aty = fn->getFunctionType()->getParamType(i+thisOffset); | |
45 if (a->getType() != aty) | |
46 { | 262 { |
47 Logger::cout() << "expected: " << *aty << '\n'; | 263 Expression* exp = (Expression*)arguments->data[i]; |
48 Logger::cout() << "got: " << *a->getType() << '\n'; | 264 DValue* expelem = exp->toElem(gIR); |
49 a = DtoBitCast(a, aty); | 265 // cast to va_list* |
266 LLValue* val = DtoBitCast(expelem->getLVal(), getVoidPtrType()); | |
267 ++argiter; | |
268 args.push_back(val); | |
50 } | 269 } |
51 args.push_back(a); | 270 } |
52 if (fnarg && fnarg->llvmByVal) | 271 |
53 palist = palist.addAttr(i+thisOffset+1, llvm::ParamAttr::ByVal); // return,this,args... | 272 // d style varargs needs a few more hidden arguments as well as special passing |
54 } | 273 else if (dvarargs) |
55 | 274 { |
56 CallOrInvoke* call = gIR->CreateCallOrInvoke(fn, args.begin(), args.end(), "tmp"); | 275 DtoBuildDVarArgList(args, palist, tf, arguments, argiter-argbegin+1); |
57 call->setCallingConv(DtoCallingConv(LINKd)); | 276 } |
277 | |
278 // otherwise we're looking at a normal function call | |
279 else | |
280 { | |
281 Logger::println("doing normal arguments"); | |
282 for (int i=0; i<arguments->dim; i++) { | |
283 int j = argiter-argbegin; | |
284 Argument* fnarg = Argument::getNth(tf->parameters, i); | |
285 DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); | |
286 LLValue* arg = argval->getRVal(); | |
287 if (fnarg && arg->getType() != callableTy->getParamType(j)) | |
288 arg = DtoBitCast(arg, callableTy->getParamType(j)); | |
289 if (fnarg && fnarg->llvmByVal) | |
290 palist = palist.addAttr(j+1, llvm::ParamAttr::ByVal); | |
291 ++argiter; | |
292 args.push_back(arg); | |
293 } | |
294 } | |
295 | |
296 #if 0 | |
297 Logger::println("%d params passed", n); | |
298 for (int i=0; i<args.size(); ++i) { | |
299 assert(args[i]); | |
300 Logger::cout() << "arg["<<i<<"] = " << *args[i] << '\n'; | |
301 } | |
302 #endif | |
303 | |
304 // void returns cannot not be named | |
305 const char* varname = ""; | |
306 if (callableTy->getReturnType() != LLType::VoidTy) | |
307 varname = "tmp"; | |
308 | |
309 //Logger::cout() << "Calling: " << *funcval << '\n'; | |
310 | |
311 // call the function | |
312 CallOrInvoke* call = gIR->CreateCallOrInvoke(callable, args.begin(), args.end(), varname); | |
313 | |
314 // get return value | |
315 LLValue* retllval = (retinptr) ? args[0] : call->get(); | |
316 | |
317 // if the type of retllval is abstract, refine to concrete | |
318 if (retllval->getType()->isAbstract()) | |
319 retllval = DtoBitCast(retllval, getPtrToType(DtoType(resulttype)), "retval"); | |
320 | |
321 // set calling convention | |
322 if (dfnval && dfnval->func) | |
323 { | |
324 int li = dfnval->func->llvmInternal; | |
325 if (li != LLVMintrinsic && li != LLVMva_start && li != LLVMva_intrinsic) | |
326 { | |
327 call->setCallingConv(callconv); | |
328 } | |
329 } | |
330 else | |
331 { | |
332 call->setCallingConv(callconv); | |
333 } | |
334 | |
335 // param attrs | |
58 call->setParamAttrs(palist); | 336 call->setParamAttrs(palist); |
59 | 337 |
60 return new DImValue(type, call->get(), false); | 338 return new DImValue(resulttype, retllval, false); |
61 } | 339 } |
340 | |
341 | |
342 | |
343 | |
344 | |
345 | |
346 | |
347 | |
348 | |
349 | |
350 | |
351 | |
352 |