// 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) {
return ImmutableCallSite(I).arg_size();
}
const Value *getActuallyCalledValue(const Instruction *I) {
const Value *CV = ImmutableCallSite(I).getCalledValue();
// 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>(CV->stripPointerCasts())) {
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 {
if (isAbsolute(CV)) return "abort(); /* segfault, call an absolute addr */";
// 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 += ",";
}
// this is an ffi call if we need casts, and it is not a Math_ builtin (with just 1 arg - Math with more args is different XXX)
bool FFI = NeedCasts && (NumArgs > 1 || Name.find("Math_") != 0);
unsigned FFI_OUT = FFI ? ASM_FFI_OUT : 0;
for (int i = 0; i < NumArgs; i++) {
if (!NeedCasts) {
text += getValueAsStr(CI->getOperand(i));
} else {
text += getValueAsCastParenStr(CI->getOperand(i), ASM_NONSPECIFIC | FFI_OUT);
}
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
getAssignIfNeeded(CI); // 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()) {
unsigned FFI_IN = FFI ? ASM_FFI_IN : 0;
text <