//===- ExpandI64.cpp - Expand out variable argument function calls-----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===------------------------------------------------------------------===//
//
// This pass expands and lowers all operations on integers larger than i64
// into 32-bit operations that can be handled by JS in a natural way.
//
// 64-bit variables become pairs of 2 32-bit variables, for the low and
// high 32 bit chunks. This happens for both registers and function
// arguments. Function return values become a return of the low 32 bits
// and a store of the high 32-bits in tempRet0, a global helper variable.
// Larger values become more chunks of 32 bits. Currently we require that
// types be a multiple of 32 bits.
//
// Many operations then become simple pairs of operations, for example
// bitwise AND becomes and AND of each 32-bit chunk. More complex operations
// like addition are lowered into calls into library support code in
// Emscripten (i64Add for example).
//
//===------------------------------------------------------------------===//
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/NaCl.h"
#include <map>
#include "llvm/Support/raw_ostream.h"
#include <stdio.h>
#define dump(x) fprintf(stderr, x "\n")
#define dumpv(x, ...) fprintf(stderr, x "\n", __VA_ARGS__)
#define dumpfail(x) { fprintf(stderr, x "\n"); fprintf(stderr, "%s : %d\n", __FILE__, __LINE__); report_fatal_error("fail"); }
#define dumpfailv(x, ...) { fprintf(stderr, x "\n", __VA_ARGS__); fprintf(stderr, "%s : %d\n", __FILE__, __LINE__); report_fatal_error("fail"); }
#define dumpIR(value) { \
std::string temp; \
raw_string_ostream stream(temp); \
stream << *(value); \
fprintf(stderr, "%s\n", temp.c_str()); \
}
#undef assert
#define assert(x) { if (!(x)) dumpfail(#x); }
using namespace llvm;
namespace {
typedef SmallVector<Value*, 2> ChunksVec;
typedef std::vector<Instruction*> SplitInstrs;
// The tricky part in all this pass is that we legalize many instructions that interdepend on each
// other. So we do one pass where we create the new legal instructions but leave the illegal ones
// in place, then a second where we hook up the legal ones to the other legal ones, and only
// then do we remove the illegal ones.
struct SplitInfo {
SplitInstrs ToFix; // new instrs, which we fix up later with proper legalized input (if they received illegal input)
ChunksVec Chunks; // 32-bit chunks of the legalized output, if the output was illegal
};
typedef std::map<Instruction*, SplitInfo> SplitsMap;
typedef std::map<Value*, ChunksVec> ArgsMap;
// This is a ModulePass because the pass recreates functions in
// order to expand i64 arguments to pairs of i32s.
class ExpandI64 : public ModulePass {
SplitsMap Splits; // old illegal value to new insts
ArgsMap SplitArgs; // old illegal function arguments, to split parts
// If the function has an illegal return or argument, create a legal version
void ensureLegalFunc(Function *F);
// If a function is illegal, remove it
void removeIllegalFunc(Function *F);
// splits an illegal instruction into 32-bit chunks. We do
// not yet have the values yet, as they depend on other
// splits, so store the parts in Splits, for FinalizeInst.
void splitInst(Instruction *I, DataLayout& DL);
// For an illegal value, returns the split out chunks
// representing the low and high parts, that splitInst
// generated.
// The value can also be a constant, in which case we just
// split it, or a function argument, in which case we
// map to the proper legalized new arguments
ChunksVec getChunks(Value *V);
void finalizeInst(Instruction *I);
Function *Add, *Sub, *Mul, *SDiv, *UDiv, *SRem, *URem, *LShr, *AShr, *Shl, *GetHigh, *SetHigh, *FtoILow, *FtoIHigh, *DtoILow, *DtoIHigh, *SItoF, *UItoF, *SItoD, *UItoD, *BItoD, *BDtoILow, *BDtoIHigh;
void ensureFuncs();
Module *TheModule;
public:
static char ID;
ExpandI64() : ModulePass(ID) {
initializeExpandI64Pass(*PassRegistry::getPassRegistry());
Add = Sub = Mul = SDiv = UDiv = SRem = URem = LShr = AShr = Shl = GetHigh = SetHigh = NULL;
}
virtual bool runOnModule(Module &M);
};
}
char ExpandI64::ID = 0;
INITIALIZE_PASS(ExpandI64, "expand-illegal-ints",
"Expand and lower illegal >i32 operations into 32-bit chunks",
false, false)
// Utilities
static bool isIllegal(Type *T) {
return T->isIntegerTy() && T->getIntegerBitWidth() > 32;
}
static FunctionType *getLegalizedFunctionType(FunctionType *FT) {
SmallVector<Type*, 0> ArgTypes; // XXX
int Num = FT->getNumParams();
for (int i = 0; i < Num; i++) {
Type *T = FT->getParamType(i);
if (!isIllegal(T)) {
ArgTypes.push_back(T);
} else {
Type