// Call handlers: flexible map of call targets to arbitrary handling code
//
// Each handler needs DEF_CALL_HANDLER and SETUP_CALL_HANDLER
//
typedef std::string (JSWriter::*CallHandler)(const Instruction*, std::string Name, int NumArgs);
typedef std::map<std::string, CallHandler> CallHandlerMap;
CallHandlerMap *CallHandlers;
// Definitions
unsigned getNumArgOperands(const Instruction *I) {
if (const CallInst *CI = dyn_cast<const CallInst>(I)) {
return CI->getNumArgOperands();
} else {
return cast<const InvokeInst>(I)->getNumArgOperands();
}
}
const Value *getActuallyCalledValue(const Instruction *I) {
const Value *CV = NULL;
if (const CallInst *CI = dyn_cast<const CallInst>(I)) {
CV = CI->getCalledValue();
} else {
CV = cast<const InvokeInst>(I)->getCalledValue();
}
if (const BitCastInst *B = dyn_cast<const BitCastInst>(CV)) {
// if the called value is a bitcast of a function, then we just call it directly, properly
// for example, extern void x() in C will turn into void x(...) in LLVM IR, then the IR bitcasts
// it to the proper form right before the call. this both causes an unnecessary indirect
// call, and it is done with the wrong type. TODO: don't even put it into the function table
if (const Function *F = dyn_cast<const Function>(B->getOperand(0))) {
CV = F;
}
}
return CV;
}
#define DEF_CALL_HANDLER(Ident, Code) \
std::string CH_##Ident(const Instruction *CI, std::string Name, int NumArgs=-1) { Code }
DEF_CALL_HANDLER(__default__, {
if (!CI) return ""; // we are just called from a handler that was called from getFunctionIndex, only to ensure the handler was run at least once
const Value *CV = getActuallyCalledValue(CI);
bool NeedCasts;
FunctionType *FT;
bool Invoke = false;
if (InvokeState == 1) {
InvokeState = 2;
Invoke = true;
}
std::string Sig;
const Function *F = dyn_cast<const Function>(CV);
if (F) {
NeedCasts = F->isDeclaration(); // if ffi call, need casts
FT = F->getFunctionType();
} else {
// function pointer call
FT = dyn_cast<FunctionType>(dyn_cast<PointerType>(CV->getType())->getElementType());
ensureFunctionTable(FT);
if (!Invoke) {
Sig = getFunctionSignature(FT, &Name);
Name = std::string("FUNCTION_TABLE_") + Sig + "[" + Name + " & #FM_" + Sig + "#]";
NeedCasts = false; // function table call, so stays in asm module
}
}
if (Invoke) {
Sig = getFunctionSignature(FT, &Name);
Name = "invoke_" + Sig;
NeedCasts = true;
}
std::string text = Name + "(";
if (NumArgs == -1) NumArgs = getNumArgOperands(CI);
if (Invoke) {
// add first param
if (F) {
text += utostr(getFunctionIndex(F)); // convert to function pointer
} else {
text += getValueAsCastStr(CV); // already a function pointer
}
if (NumArgs > 0) text += ",";
}
for (int i = 0; i < NumArgs; i++) {
if (!NeedCasts) {
text += getValueAsStr(CI->getOperand(i));
} else {
text += getValueAsCastStr(CI->getOperand(i), ASM_NONSPECIFIC);
}
if (i < NumArgs - 1) text += ",";
}
text += ")";
// handle return value
Type *InstRT = CI->getType();
Type *ActualRT = FT->getReturnType();
if (!InstRT->isVoidTy() && ActualRT->isVoidTy()) {
// the function we are calling was cast to something returning a value, but it really
// does not return a value
getAssign(getJSName(CI), InstRT); // ensure the variable is defined, but do not emit it here
// it should have 0 uses, but just to be safe
} else if (!ActualRT->isVoidTy()) {
text = getAssign(getJSName(CI), ActualRT) + getCast(text, ActualRT, ASM_NONSPECIFIC);
}
return text;
})
// exceptions support
DEF_CALL_HANDLER(emscripten_preinvoke, {
assert(InvokeState == 0);
InvokeState = 1;
return "__THREW__ = 0";
})
DEF_CALL_HANDLER(emscripten_postinvoke, {
assert(InvokeState == 1 || InvokeState == 2); // normally 2, but can be 1 if the call in between was optimized out
InvokeState = 0;
return getAssign(getJSName