1 #ifndef MAGIC_MEM_FUNCTION_H
2 #define MAGIC_MEM_FUNCTION_H
3
4 #include <pass.h>
5 #include <magic/support/TypeInfo.h>
6
7 #define NUM_MAGIC_ARGS 3
8
9 using namespace llvm;
10
11 namespace llvm {
12
13 class MagicMemFunction {
14 public:
15 MagicMemFunction(Module &M, Function *function, Function *wrapper, bool isDealloc, bool isNested, int allocFlags);
16
17 Function* getFunction() const;
18 Function* getWrapper() const;
19 bool isDeallocFunction() const;
20 bool isNestedFunction() const;
21 int getAllocFlags() const;
22 Instruction* getInstruction() const;
23 Function* getInstructionParent() const;
24 TypeInfo* getInstructionTypeInfo() const;
25 Value* getInstructionTypeValue() const;
26 bool hasInstructionType() const;
27 std::vector<MagicMemFunction> getInstructionDeps() const;
28
29 void setInstruction(Instruction* I);
30 void setInstructionTypeInfo(TypeInfo* aTypeInfo, std::string &allocName, std::string &allocParentName);
31 void setInstructionTypeValue(Value* typeValue, Value* allocNameValue, Value* allocParentNameValue);
32 void addInstructionDep(MagicMemFunction &function);
33 void replaceInstruction(std::map<TypeInfo*, Constant*> &magicArrayTypePtrMap, TypeInfo *voidPtrTypeInfo);
34
35 void print(raw_ostream &OS) const;
36 void printDescription(raw_ostream &OS) const;
37 const std::string getDescription() const;
38
39 static int getMemFunctionPointerParam(Function* function, std::set<Function*> &brkFunctions, TypeInfo *voidPtrTypeInfo);
40 static Function* getCustomWrapper(Function* function, Function* stdFunction, Function* stdWrapper, std::vector<unsigned> argMapping,
41 bool isDealloc);
42 static bool isCustomWrapper(Function *function);
43
44 private:
45 Module *module;
46 Function *function;
47 Function *wrapper;
48 bool isDealloc;
49 bool isNested;
50 int allocFlags;
51 Instruction *instruction;
52 TypeInfo* aTypeInfo;
53 std::string allocName;
54 std::string allocParentName;
55 Value* typeValue;
56 Value* allocNameValue;
57 Value* allocParentNameValue;
58 std::vector<MagicMemFunction> instructionDeps;
59
60 void buildWrapper(std::map<TypeInfo*, Constant*> &magicArrayTypePtrMap, TypeInfo *voidPtrTypeInfo);
61
62 static Function *lastAllocWrapper;
63 static std::map<std::string, Function*> allocWrapperCache;
64 static std::set<Function*> customWrapperSet;
65 };
66
67 inline raw_ostream &operator<<(raw_ostream &OS, const MagicMemFunction &aMagicMemFunction) {
68 aMagicMemFunction.print(OS);
69 return OS;
70 }
71
print(raw_ostream & OS)72 inline void MagicMemFunction::print(raw_ostream &OS) const {
73 OS << getDescription();
74 }
75
printDescription(raw_ostream & OS)76 inline void MagicMemFunction::printDescription(raw_ostream &OS) const {
77 OS << "[ function = ";
78 OS << function->getName() << "(" << TypeUtil::getDescription(function->getFunctionType()) << ")";
79 OS << ", wrapper = ";
80 if (wrapper) {
81 OS << wrapper->getName() << "(" << TypeUtil::getDescription(wrapper->getFunctionType()) << ")";
82 } else
83 OS << "NULL";
84 OS << ", isDeallocFunction = ";
85 OS << isDealloc;
86 OS << ", isNestedFunction = ";
87 OS << isNested;
88 OS << ", instruction = ";
89 if (instruction)
90 instruction->print(OS);
91 else
92 OS << "NULL";
93 OS << ", typeInfo = ";
94 if (aTypeInfo)
95 OS << aTypeInfo->getDescription();
96 else
97 OS << "NULL";
98 OS << ", allocName = ";
99 OS << allocName;
100 OS << ", allocParentName = ";
101 OS << allocParentName;
102 OS << ", typeValue = ";
103 if (typeValue)
104 typeValue->print(OS);
105 else
106 OS << "NULL";
107 OS << ", allocNameValue = ";
108 if (allocNameValue)
109 allocNameValue->print(OS);
110 else
111 OS << "NULL";
112 OS << ", allocParentNameValue = ";
113 if (allocParentNameValue)
114 allocParentNameValue->print(OS);
115 else
116 OS << "NULL";
117 OS << ", instructionDeps = {";
118 for (unsigned i = 0; i < instructionDeps.size(); i++) {
119 if (i > 0) {
120 OS << ", ";
121 }
122 instructionDeps[i].print(OS);
123 }
124 OS << "}]";
125 }
126
getDescription()127 inline const std::string MagicMemFunction::getDescription() const {
128 std::string string;
129 raw_string_ostream ostream(string);
130 printDescription(ostream);
131 ostream.flush();
132 return string;
133 }
134
MagicMemFunction(Module & M,Function * function,Function * wrapper,bool isDealloc,bool isNested,int allocFlags)135 inline MagicMemFunction::MagicMemFunction(Module &M, Function *function, Function *wrapper, bool isDealloc, bool isNested, int allocFlags) {
136 this->module = &M;
137 this->function = function;
138 this->wrapper = wrapper;
139 this->isDealloc = isDealloc;
140 this->isNested = isNested;
141 this->allocFlags = allocFlags;
142 this->instruction = NULL;
143 this->aTypeInfo = NULL;
144 this->allocName = "";
145 this->allocParentName = "";
146 this->typeValue = NULL;
147 this->allocNameValue = NULL;
148 this->allocParentNameValue = NULL;
149 assert(function);
150 if (wrapper && !isDealloc && !isNested) {
151 lastAllocWrapper = wrapper;
152 }
153 if (isDealloc) {
154 assert(!allocFlags);
155 }
156 }
157
getFunction()158 inline Function* MagicMemFunction::getFunction() const {
159 return function;
160 }
161
getWrapper()162 inline Function* MagicMemFunction::getWrapper() const {
163 return wrapper;
164 }
165
isDeallocFunction()166 inline bool MagicMemFunction::isDeallocFunction() const {
167 return isDealloc;
168 }
169
isNestedFunction()170 inline bool MagicMemFunction::isNestedFunction() const {
171 return isNested;
172 }
173
getAllocFlags()174 inline int MagicMemFunction::getAllocFlags() const {
175 return allocFlags;
176 }
177
getInstruction()178 inline Instruction* MagicMemFunction::getInstruction() const {
179 return instruction;
180 }
181
getInstructionParent()182 inline Function* MagicMemFunction::getInstructionParent() const {
183 if (!instruction) {
184 return NULL;
185 }
186 return instruction->getParent()->getParent();
187 }
188
getInstructionTypeInfo()189 inline TypeInfo* MagicMemFunction::getInstructionTypeInfo() const {
190 return aTypeInfo;
191 }
192
getInstructionTypeValue()193 inline Value* MagicMemFunction::getInstructionTypeValue() const {
194 return typeValue;
195 }
196
hasInstructionType()197 inline bool MagicMemFunction::hasInstructionType() const {
198 return aTypeInfo || typeValue;
199 }
200
getInstructionDeps()201 inline std::vector<MagicMemFunction> MagicMemFunction::getInstructionDeps() const {
202 return instructionDeps;
203 }
204
setInstruction(Instruction * I)205 inline void MagicMemFunction::setInstruction(Instruction* I) {
206 this->instruction = I;
207 assert(isa<CallInst>(instruction) || isa<InvokeInst>(instruction));
208 }
209
setInstructionTypeInfo(TypeInfo * aTypeInfo,std::string & allocName,std::string & allocParentName)210 inline void MagicMemFunction::setInstructionTypeInfo(TypeInfo* aTypeInfo, std::string &allocName, std::string &allocParentName) {
211 this->aTypeInfo = aTypeInfo;
212 this->allocName = allocName;
213 this->allocParentName = allocParentName;
214 }
215
setInstructionTypeValue(Value * typeValue,Value * allocNameValue,Value * allocParentNameValue)216 inline void MagicMemFunction::setInstructionTypeValue(Value* typeValue, Value* allocNameValue, Value* allocParentNameValue) {
217 this->typeValue = typeValue;
218 this->allocNameValue = allocNameValue;
219 this->allocParentNameValue = allocParentNameValue;
220 }
221
addInstructionDep(MagicMemFunction & function)222 inline void MagicMemFunction::addInstructionDep(MagicMemFunction &function) {
223 assert(wrapper == NULL && "Dependencies are resolved at wrapper building time, so wrapper has to be NULL!");
224 instructionDeps.push_back(function);
225 allocFlags |= function.getAllocFlags();
226 }
227
replaceInstruction(std::map<TypeInfo *,Constant * > & magicArrayTypePtrMap,TypeInfo * voidPtrTypeInfo)228 inline void MagicMemFunction::replaceInstruction(std::map<TypeInfo*, Constant*> &magicArrayTypePtrMap, TypeInfo *voidPtrTypeInfo) {
229 Instruction *I = getInstruction();
230 assert(I);
231 CallSite CS = MagicUtil::getCallSiteFromInstruction(I);
232 std::vector<Value*> magicMemArgs;
233 unsigned numMagicArgs = 0;
234 //if we do not have a wrapper, build one
235 if (!wrapper) {
236 buildWrapper(magicArrayTypePtrMap, voidPtrTypeInfo);
237 }
238 //inject magic args
239 if (!isDeallocFunction() && !isNestedFunction()) {
240 std::map<TypeInfo*, Constant*>::iterator it;
241 if (!typeValue) {
242 assert(aTypeInfo);
243 if (aTypeInfo == voidPtrTypeInfo->getContainedType(0)) {
244 typeValue = ConstantPointerNull::get((TYPECONST PointerType*) (wrapper->arg_begin()->getType()));
245 } else {
246 it = magicArrayTypePtrMap.find(aTypeInfo);
247 assert(it != magicArrayTypePtrMap.end());
248 typeValue = it->second;
249 }
250 assert(allocName.compare(""));
251 assert(allocParentName.compare(""));
252 allocNameValue = MagicUtil::getArrayPtr(*module, MagicUtil::getStringRef(*module, allocName));
253 allocParentNameValue = MagicUtil::getArrayPtr(*module, MagicUtil::getStringRef(*module, allocParentName));
254 }
255 magicMemArgs.push_back(typeValue);
256 magicMemArgs.push_back(allocNameValue);
257 magicMemArgs.push_back(allocParentNameValue);
258 numMagicArgs = NUM_MAGIC_ARGS;
259 }
260 //push other args
261 unsigned arg_size = MagicUtil::getCalledFunctionFromCS(CS)->getFunctionType()->getNumContainedTypes() - 1;
262 for (unsigned i = 0; i < arg_size; i++) {
263 Value *arg = CS.getArgument(i);
264 TYPECONST Type* wArgType = wrapper->getFunctionType()->getContainedType(i + numMagicArgs + 1);
265 if (arg->getType() != wArgType) {
266 if (arg->getType()->isPointerTy()) {
267 assert(wArgType->isPointerTy());
268 arg = CastInst::CreatePointerCast(arg, wArgType, "WrapperCast", I);
269 }
270 else {
271 assert(arg->getType()->isIntegerTy());
272 assert(wArgType->isIntegerTy());
273 arg = CastInst::CreateIntegerCast(arg, wArgType, false, "WrapperCast", I);
274 }
275 }
276 magicMemArgs.push_back(arg);
277 }
278 //replace function with wrapper
279 CallInst* newInst = MagicUtil::createCallInstruction(wrapper, magicMemArgs, "", I);
280 newInst->takeName(I);
281 MagicUtil::replaceCallInst(I, newInst, NUM_MAGIC_ARGS);
282 }
283
getMemFunctionPointerParam(Function * function,std::set<Function * > & brkFunctions,TypeInfo * voidPtrTypeInfo)284 inline int MagicMemFunction::getMemFunctionPointerParam(Function* function, std::set<Function*> &brkFunctions, TypeInfo *voidPtrTypeInfo) {
285 TYPECONST Type *type = function->getReturnType();
286 if (type == voidPtrTypeInfo->getType()) {
287 return 0;
288 } else if (brkFunctions.find(function) != brkFunctions.end()) {
289 return 1;
290 } else {
291 unsigned i;
292 for (i = 1; i < function->getFunctionType()->getNumContainedTypes(); i++) {
293 type = function->getFunctionType()->getContainedType(i);
294 if (type->isPointerTy() && type->getContainedType(0) == voidPtrTypeInfo->getType()) {
295 return i;
296 }
297 }
298 }
299
300 return -1;
301 }
302
buildWrapper(std::map<TypeInfo *,Constant * > & magicArrayTypePtrMap,TypeInfo * voidPtrTypeInfo)303 inline void MagicMemFunction::buildWrapper(std::map<TypeInfo*, Constant*> &magicArrayTypePtrMap, TypeInfo *voidPtrTypeInfo) {
304 assert(!isDeallocFunction());
305 assert(!isNestedFunction());
306 assert(lastAllocWrapper);
307 std::vector<TYPECONST Type*> ArgTypes;
308 VALUE_TO_VALUE_MAP_TY VMap;
309
310 std::map<std::string, Function*>::iterator allocWrapperCacheIt;
311
312 // See if the wrapper is in cache, otherwise create a new wrapper using function cloning
313 allocWrapperCacheIt = allocWrapperCache.find(function->getName());
314 if (allocWrapperCacheIt != allocWrapperCache.end()) {
315 wrapper = allocWrapperCacheIt->second;
316 return;
317 }
318
319 // Build arg types for wrapper
320 Function::const_arg_iterator E = lastAllocWrapper->arg_begin();
321 for (unsigned i = 0; i < NUM_MAGIC_ARGS; i++)
322 E++;
323 for (Function::const_arg_iterator I = lastAllocWrapper->arg_begin(); I != E; ++I)
324 ArgTypes.push_back(I->getType());
325 E = function->arg_end();
326 for (Function::const_arg_iterator I = function->arg_begin(); I != E; ++I)
327 ArgTypes.push_back(I->getType());
328
329 // Create a new function type...
330 FunctionType *FTy = FunctionType::get(function->getFunctionType()->getReturnType(), ArgTypes, function->getFunctionType()->isVarArg());
331
332 // Create the wrapper
333 wrapper = Function::Create(FTy, function->getLinkage(), "magic_" + function->getName(), function->getParent());
334
335 // Loop over the arguments, copying the names of the mapped arguments over...
336 Function::arg_iterator DestI = wrapper->arg_begin();
337 Value *magicTypeValue = DestI;
338 magicTypeValue->setName("magic_type");
339 DestI++;
340 Value *magicNameValue = DestI;
341 magicNameValue->setName("magic_name");
342 DestI++;
343 Value *magicParentNameValue = DestI;
344 magicParentNameValue->setName("magic_parent_name");
345 DestI++;
346 for (Function::const_arg_iterator I = function->arg_begin(), E = function->arg_end(); I != E; ++I) {
347 DestI->setName(I->getName());
348 VMap[I] = DestI++;
349 }
350
351 SmallVector<ReturnInst*, 8> Returns; // Ignore returns cloned...
352 CloneFunctionInto(wrapper, function, VMap, false, Returns, "", NULL);
353
354 allocWrapperCache.insert(std::pair<std::string, Function*>(function->getName(), wrapper));
355
356 // Create a mapping between the function instruction pointers and the wrapper instruction pointers
357 std::vector<Instruction *> wrapperInstructionDeps;
358 for (unsigned i = 0; i < instructionDeps.size(); i++) {
359 Instruction *instruction = instructionDeps[i].getInstruction();
360 Instruction *wrapperInstruction = NULL;
361 unsigned instructionOffset = 0;
362 for (inst_iterator I = inst_begin(function), E = inst_end(function); I != E; ++I, instructionOffset++) {
363 if (instruction == &(*I)) {
364 break;
365 }
366 }
367 assert(instructionOffset > 0);
368 for (inst_iterator I = inst_begin(wrapper), E = inst_end(wrapper); I != E; ++I, instructionOffset--) {
369 if (instructionOffset == 0) {
370 wrapperInstruction = &(*I);
371 break;
372 }
373 }
374 assert(wrapperInstruction);
375 wrapperInstructionDeps.push_back(wrapperInstruction);
376 }
377
378 // Forward magic type argument to any dependent instruction and replace it
379 for (unsigned i = 0; i < wrapperInstructionDeps.size(); i++) {
380 instructionDeps[i].setInstruction(wrapperInstructionDeps[i]);
381 instructionDeps[i].setInstructionTypeValue(magicTypeValue, magicNameValue, magicParentNameValue);
382 instructionDeps[i].replaceInstruction(magicArrayTypePtrMap, voidPtrTypeInfo);
383 }
384 }
385
getCustomWrapper(Function * function,Function * stdFunction,Function * stdWrapper,std::vector<unsigned> argMapping,bool isDealloc)386 inline Function* MagicMemFunction::getCustomWrapper(Function* function, Function* stdFunction, Function* stdWrapper, std::vector<unsigned> argMapping,
387 bool isDealloc) {
388 Function* wrapper;
389 std::vector<TYPECONST Type*> ArgTypes;
390 VALUE_TO_VALUE_MAP_TY VMap;
391
392 // Build arg types for wrapper
393 // add magic arguments
394 if (!isDealloc) {
395 Function::const_arg_iterator E = stdWrapper->arg_begin();
396 for (unsigned i = 0; i < NUM_MAGIC_ARGS; i++)
397 E++;
398 for (Function::const_arg_iterator I = stdWrapper->arg_begin(); I != E; ++I) {
399 ArgTypes.push_back(I->getType());
400 }
401 }
402 // add original function arguments
403 for (Function::const_arg_iterator I = function->arg_begin(), E = function->arg_end(); I != E; ++I) {
404 ArgTypes.push_back(I->getType());
405 }
406
407 // Create a new function type...
408 FunctionType *FTy = FunctionType::get(stdWrapper->getFunctionType()->getReturnType(), ArgTypes, function->getFunctionType()->isVarArg());
409
410 // Create the wrapper
411 wrapper = Function::Create(FTy, function->getLinkage(), "magic_" + function->getName(), function->getParent());
412
413 // Loop over the arguments, copying the names of the mapped arguments over...
414 Function::arg_iterator DestI = wrapper->arg_begin();
415 std::vector<Value*> wrapperArgs;
416 if (!isDealloc) {
417 std::string magicArgs[] = { "magic_type", "magic_name", "magic_parent_name" };
418 for (unsigned i = 0; i < NUM_MAGIC_ARGS; i++) {
419 DestI->setName(magicArgs[i]);
420 wrapperArgs.push_back(DestI);
421 DestI++;
422 }
423 }
424 for (Function::const_arg_iterator I = function->arg_begin(), E = function->arg_end(); I != E; ++I) {
425 DestI->setName(I->getName());
426 wrapperArgs.push_back(DestI);
427 DestI++;
428 }
429
430 // map the arguments of the standard wrapper to the arguments of the new custom wrapper
431 if ((!isDealloc) || argMapping.size()) {
432 Function::const_arg_iterator W = stdWrapper->arg_begin();
433 if (!isDealloc) {
434 // magic arguments are in the same position
435 for (unsigned i = 0; i < NUM_MAGIC_ARGS; i++) {
436 VMap[W] = wrapperArgs[i];
437 W++;
438 }
439 }
440 // map the selected arguments of the custom wrapper using the mapping provided as input
441 unsigned argOffset = isDealloc ? 0 : NUM_MAGIC_ARGS;
442 for (unsigned i = 0; i < argMapping.size(); i++) {
443 VMap[W] = wrapperArgs[argOffset + argMapping[i] - 1];
444 W++;
445 }
446 }
447
448 SmallVector<ReturnInst*, 8> Returns; // Ignore returns cloned...
449 CloneFunctionInto(wrapper, stdWrapper, VMap, false, Returns, "", NULL);
450
451 // check whether some of the arguments of the custom wrapper need to be casted
452 // in order to match the basic wrapper implementation
453 Instruction *FirstInst = MagicUtil::getFirstNonAllocaInst(wrapper);
454 Function::const_arg_iterator W = stdWrapper->arg_begin();
455 unsigned argOffset = 0;
456 if (!isDealloc) {
457 argOffset = NUM_MAGIC_ARGS;
458 // skip the magic arguments, they are always the same
459 for (unsigned i = 0; i < NUM_MAGIC_ARGS; i++) {
460 W++;
461 }
462 }
463 for (unsigned i = 0; i < argMapping.size(); i++) {
464 TYPECONST Type* StdParamType = W->getType();
465 Value* ParamValue = wrapperArgs[argOffset + argMapping[i] - 1];
466 TYPECONST Type* ParamType = ParamValue->getType();
467 if (!MagicUtil::isCompatibleType(ParamType, StdParamType)) {
468 assert(CastInst::isCastable(ParamType, StdParamType) && "The type of the parameter of the custom wrapper "
469 "cannot be casted to the type of the basic wrapper to which it is corresponding.");
470 Instruction::CastOps CastOpCode = CastInst::getCastOpcode(ParamValue, false, StdParamType, false);
471 Instruction *ParamCastInst = CastInst::Create(CastOpCode, ParamValue, StdParamType, "", FirstInst);
472
473 for (Value::use_iterator it = ParamValue->use_begin(); it != ParamValue->use_end(); it++) {
474 if (Constant * C = dyn_cast<Constant>(*it)) {
475 if (!isa<GlobalValue>(C)) {
476 C->replaceUsesOfWith(ParamValue, ParamCastInst);
477 continue;
478 }
479 }
480 Instruction *I = dyn_cast<Instruction>(*it);
481 if (I && (I != ParamCastInst)) {
482 // replace all uses, except for the calls to the wrapped function
483 CallInst *CI = dyn_cast<CallInst>(I);
484 if (CI && (CI->getCalledFunction() == function)) {
485 continue;
486 }
487 I->replaceUsesOfWith(ParamValue, ParamCastInst);
488 }
489 }
490 }
491 W++;
492 }
493
494 // replace the call(s) to the standard function with calls to our function
495 for (Function::iterator BI = wrapper->getBasicBlockList().begin(), BE = wrapper->getBasicBlockList().end(); BI != BE; ++BI) {
496 unsigned pos = 0;
497 unsigned bbSize = BI->getInstList().size();
498 while (pos < bbSize) {
499 BasicBlock::iterator it = BI->getInstList().begin();
500 for (unsigned i = 0; i < pos; i++) {
501 it++;
502 }
503 Instruction *inst = &(*it);
504 // find the calls to the standard function
505 CallInst *callInst = dyn_cast<CallInst>(inst);
506 if (callInst && callInst->getCalledFunction() && (callInst->getCalledFunction()->getFunctionType() == stdFunction->getFunctionType())
507 && (!callInst->getCalledFunction()->getName().compare(stdFunction->getName()))) {
508 CallSite CS = MagicUtil::getCallSiteFromInstruction(callInst);
509 unsigned numStdParams = stdFunction->getFunctionType()->getNumParams();
510 unsigned numParams = function->getFunctionType()->getNumParams();
511 // construct the parameter array
512 std::vector<Value*> callArgs(numParams, NULL);
513 // first add the arguments that are common to the custom and standard function
514 // add casts where necessary
515 for (unsigned i = 0; i < numStdParams; i++) {
516 Value *argValue = CS.getArgument(i);
517 TYPECONST Type* paramType = function->getFunctionType()->getParamType(i);
518 TYPECONST Type* argType = argValue->getType();
519 if (paramType != argType) {
520 assert(CastInst::isCastable(argType, paramType) && "The value of the argument cannot be "
521 "casted to the parameter type required by the function to be called.");
522 Instruction::CastOps opcode = CastInst::getCastOpcode(argValue, false, paramType, false);
523 argValue = CastInst::Create(opcode, argValue, paramType, "", callInst);
524 }
525 callArgs[argMapping[i] - 1] = argValue;
526 }
527 // the other arguments are just forwarded from the wrapper's argument list
528 // skip the magic arguments of the wrapper from the beginning of the argument list
529 unsigned argOffset = isDealloc ? 0 : NUM_MAGIC_ARGS;
530 for (unsigned i = argOffset; i < wrapper->getFunctionType()->getNumParams(); i++) {
531 if (callArgs[i - argOffset] == NULL) {
532 Value* arg = wrapperArgs[i];
533 callArgs[i - argOffset] = arg;
534 }
535 }
536
537 CallInst* newCallInst = MagicUtil::createCallInstruction(function, callArgs, "", callInst);
538 newCallInst->takeName(callInst);
539 MagicUtil::replaceCallInst(callInst, newCallInst, argOffset);
540 }
541 pos++;
542 }
543 }
544
545 customWrapperSet.insert(wrapper);
546 return wrapper;
547 }
548
isCustomWrapper(Function * function)549 inline bool MagicMemFunction::isCustomWrapper(Function *function)
550 {
551 return customWrapperSet.find(function) != customWrapperSet.end();
552 }
553
554 }
555
556 #endif
557
558