diff options
-rw-r--r-- | lib/Transforms/Scalar/NaClCcRewrite.cpp | 382 |
1 files changed, 278 insertions, 104 deletions
diff --git a/lib/Transforms/Scalar/NaClCcRewrite.cpp b/lib/Transforms/Scalar/NaClCcRewrite.cpp index 6309f7e0b2..0a3d073ff7 100644 --- a/lib/Transforms/Scalar/NaClCcRewrite.cpp +++ b/lib/Transforms/Scalar/NaClCcRewrite.cpp @@ -14,7 +14,6 @@ // Major TODOs: -// * add register constraints to x86-64 rewrite decissions // * dealing with vararg // (We shoulf exclude all var arg functions and calls to them from rewrites) @@ -25,6 +24,7 @@ #include "llvm/Constant.h" #include "llvm/Instruction.h" #include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" #include "llvm/Function.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" @@ -74,6 +74,7 @@ struct TypeRewriteRule { // C: "copy", use src as dst (only allowed for dst and sret) // F: generic function type (only allowed for src) +// TODO: consider getting rid of the "C" feature // The X8664 Rewrite rules are also subject to // register constraints, c.f.: section 3.2.3 @@ -105,6 +106,49 @@ TypeRewriteRule SretRulesARM[] = { {0, 0, 0}, }; +// Helper class to model Register Usage as required by +// the x86-64 calling conventions +class RegUse { + uint32_t n_int_; + uint32_t n_float_; + + public: + RegUse(uint32_t n_int=0, uint32_t n_float=0) : + n_int_(n_int), n_float_(n_float) {} + + static RegUse OneIntReg() { return RegUse(1, 0); } + static RegUse OnePointerReg() { return RegUse(1, 0); } + static RegUse OneFloatReg() { return RegUse(0, 1); } + + RegUse operator+(RegUse other) const { + return RegUse(n_int_ + other.n_int_, n_float_ + other.n_float_); } + RegUse operator-(RegUse other) const { + return RegUse(n_int_ - other.n_int_, n_float_ - other.n_float_); } + bool operator==(RegUse other) const { + return n_int_ == other.n_int_ && n_float_ == other.n_float_; } + bool operator!=(RegUse other) const { + return n_int_ != other.n_int_ && n_float_ != other.n_float_; } + bool operator<=(RegUse other) const { + return n_int_ <= other.n_int_ && n_float_ <= other.n_float_; } + bool operator<(RegUse other) const { + return n_int_ < other.n_int_ && n_float_ < other.n_float_; } + bool operator>=(RegUse other) const { + return n_int_ >= other.n_int_ && n_float_ >= other.n_float_; } + bool operator>(RegUse other) const { + return n_int_ > other.n_int_ && n_float_ > other.n_float_; } + RegUse& operator+=(const RegUse& other) { + n_int_ += other.n_int_; n_float_ += other.n_float_; return *this;} + RegUse& operator-=(const RegUse& other) { + n_int_ -= other.n_int_; n_float_ -= other.n_float_; return *this;} + + friend raw_ostream& operator<<(raw_ostream &O, const RegUse& reg); +}; + +raw_ostream& operator<<(raw_ostream &O, const RegUse& reg) { + O << "(" << reg.n_int_ << ", " << reg.n_float_ << ")"; + return O; +} + // TODO: Find a better way to determine the architecture const TypeRewriteRule* GetByvalRewriteRulesForTarget( const TargetLowering* tli) { @@ -137,19 +181,44 @@ const TypeRewriteRule* GetSretRewriteRulesForTarget( return 0; } +// TODO: Find a better way to determine the architecture +// Describes the number of registers available for function +// argument passing which may affect rewrite decisions on +// some platforms. +RegUse GetAvailableRegsForTarget( + const TargetLowering* tli) { + if (!FlagEnableCcRewrite) return RegUse(0, 0); + + const TargetMachine &m = tli->getTargetMachine(); + const StringRef triple = m.getTargetTriple(); + + // integer: RDI, RSI, RDX, RCX, R8, R9 + // float XMM0, ..., XMM7 + if (0 == triple.find("x86_64")) return RegUse(6, 8); + // unused + if (0 == triple.find("i686")) return RegUse(0, 0); + // no constraints enforced here - the backend handles all the details + uint32_t max = std::numeric_limits<uint32_t>::max(); + if (0 == triple.find("armv7a")) return RegUse(max, max); + + llvm_unreachable("Unknown arch"); + return 0; +} + // This class represents the a bitcode rewrite pass which ensures // that all ppapi interfaces are calling convention compatible // with gcc. This pass is archtitecture dependent. struct NaClCcRewrite : public FunctionPass { static char ID; // Pass identification, replacement for typeid - const TypeRewriteRule* SretRewriteRules; const TypeRewriteRule* ByvalRewriteRules; + const RegUse AvailableRegs; explicit NaClCcRewrite(const TargetLowering *tli = 0) : FunctionPass(ID), SretRewriteRules(GetSretRewriteRulesForTarget(tli)), - ByvalRewriteRules(GetByvalRewriteRulesForTarget(tli)) { + ByvalRewriteRules(GetByvalRewriteRulesForTarget(tli)), + AvailableRegs(GetAvailableRegsForTarget(tli)) { initializeNaClCcRewritePass(*PassRegistry::getPassRegistry()); } @@ -250,6 +319,48 @@ bool HasRewriteType(const Type* type, const char*& pattern) { } } +RegUse RegUseForRewriteRule(const TypeRewriteRule* rule) { + const char* pattern = std::string("C") == rule->dst ? rule->src : rule->dst; + RegUse result(0, 0); + while (char c = *pattern++) { + // Note, we only support a subset here, complex types (s, P) + // would require more work + switch (c) { + case 'i': + case 'l': + result += RegUse::OneIntReg(); + break; + case 'd': + case 'f': + result += RegUse::OneFloatReg(); + break; + default: + dbgs() << c << "\n"; + llvm_unreachable("unexpected return type"); + } + } + return result; +} + +// Note, this only has to be accurate for x86-64 and is intentionally +// quite strict so that we know when to add support for new types. +// Ideally, unexpected types would be flagged by a bitcode checker. +RegUse RegUseForType(const Type* t) { + if (t->isPointerTy()) { + return RegUse::OnePointerReg(); + } else if (t->isFloatTy() || t->isDoubleTy()) { + return RegUse::OneFloatReg(); + } else if (t->isIntegerTy()) { + const IntegerType* it = dyn_cast<const IntegerType>(t); + unsigned width = it->getBitWidth(); + // x86-64 assumption here - use "register info" to make this better + if (width <= 64) return RegUse::OneIntReg(); + } + + dbgs() << *const_cast<Type*>(t) << "\n"; + llvm_unreachable("unexpected type in RegUseForType"); +} + // Match a type against a set of rewrite rules. // Return the matching rule, if any. const TypeRewriteRule* MatchRewriteRules( @@ -285,28 +396,34 @@ Type* CreateFunctionPointerType(Type* result_type, // Determines whether a function body needs a rewrite bool FunctionNeedsRewrite(const Function* fun, const TypeRewriteRule* ByvalRewriteRules, - const TypeRewriteRule* SretRewriteRules) { + const TypeRewriteRule* SretRewriteRules, + RegUse available) { // TODO: can this be detected on indirect callsites as well. // if we skip the rewrite for the function body // we also need to skip it at the callsites // if (F.isVarArg()) return false; - for (Function::const_arg_iterator AI = fun->arg_begin(), AE = fun->arg_end(); AI != AE; ++AI) { const Argument& a = *AI; const Type* t = a.getType(); // byval and srets are modelled as pointers (to structs) - if (!t->isPointerTy()) continue; - Type* pointee = dyn_cast<PointerType>(t)->getElementType(); - - if (ByvalRewriteRules && a.hasByValAttr()) { - if (0 != MatchRewriteRules(pointee, ByvalRewriteRules)) return true; - } - - if (SretRewriteRules && a.hasStructRetAttr()) { - if (0 != MatchRewriteRules(pointee, SretRewriteRules)) return true; + if (t->isPointerTy()) { + Type* pointee = dyn_cast<PointerType>(t)->getElementType(); + + if (ByvalRewriteRules && a.hasByValAttr()) { + const TypeRewriteRule* rule = + MatchRewriteRules(pointee, ByvalRewriteRules); + if (rule != 0 && RegUseForRewriteRule(rule) <= available) { + return true; + } + } else if (SretRewriteRules && a.hasStructRetAttr()) { + if (0 != MatchRewriteRules(pointee, SretRewriteRules)) { + return true; + } + } } + available -= RegUseForType(t); } return false; } @@ -471,6 +588,23 @@ void UpdateFunctionSignature(Function &F, F.setAttributes(AttrListPtr::get(new_attributes_vec)); } + +void ExtractFunctionArgsAndAttributes(Function& F, + std::vector<Argument*>& old_arguments, + std::vector<Attributes>& old_attributes) { + for (Function::arg_iterator ai = F.arg_begin(), + end = F.arg_end(); + ai != end; + ++ai) { + old_arguments.push_back(ai); + } + + for (size_t i = 0; i < old_arguments.size(); ++i) { + // index zero is for return value attributes + old_attributes.push_back(F.getAttributes().getParamAttributes(i + 1)); + } +} + // Apply byval or sret rewrites to function body. void NaClCcRewrite::RewriteFunctionPrologAndEpilog(Function& F) { @@ -483,54 +617,57 @@ void NaClCcRewrite::RewriteFunctionPrologAndEpilog(Function& F) { std::vector<Argument*> new_arguments; std::vector<Attributes> new_attributes; std::vector<Argument*> old_arguments; + std::vector<Attributes> old_attributes; - // make copy first as create Argument adds them to the list - for (Function::arg_iterator ai = F.arg_begin(), - end = F.arg_end(); - ai != end; - ++ai) { - old_arguments.push_back(ai); - } - for (size_t i = 0; i < old_arguments.size(); ++i) { - Argument* arg = old_arguments[i]; - Type* t = arg->getType(); - // index zero is for return value attributes - Attributes attr = F.getAttributes().getParamAttributes(i + 1); - const TypeRewriteRule* rule = 0; - if (attr & Attribute::ByVal) { - rule = MatchRewriteRulesPointee(t, ByvalRewriteRules); - } - if (rule == 0) { - new_arguments.push_back(arg); - new_attributes.push_back(attr); - continue; - } - DEBUG(dbgs() << "REWRITING BYVAL " - << *t << " arg " << arg->getName() << " " << rule->name << "\n"); - FixFunctionByvalsParameter(F, - new_arguments, - new_attributes, - arg, - rule); - } + // make a copy of everything first as create Argument adds them to the list + ExtractFunctionArgsAndAttributes(F, old_arguments, old_attributes); // A non-zero new_result_type indicates an sret rewrite Type* new_result_type = 0; + // only the first arg can be "sret" - if (new_attributes[0] & Attribute::StructRet) { - const TypeRewriteRule* sret_rule = MatchRewriteRulesPointee( - new_arguments[0]->getType(), SretRewriteRules); + if (old_attributes.size() > 0 && old_attributes[0] & Attribute::StructRet) { + const TypeRewriteRule* sret_rule = + MatchRewriteRulesPointee(old_arguments[0]->getType(), SretRewriteRules); if (sret_rule) { - Argument* arg = F.getArgumentList().begin(); + Argument* arg = old_arguments[0]; DEBUG(dbgs() << "REWRITING SRET " << " arg " << arg->getName() << " " << sret_rule->name << "\n"); new_result_type = RewriteFunctionSret(F, arg, sret_rule); - new_arguments.erase(new_arguments.begin()); - new_attributes.erase(new_attributes.begin()); + old_arguments.erase(old_arguments.begin()); + old_attributes.erase(old_attributes.begin()); } } + // now deal with the byval arguments + RegUse available = AvailableRegs; + for (size_t i = 0; i < old_arguments.size(); ++i) { + Argument* arg = old_arguments[i]; + Type* t = arg->getType(); + Attributes attr = old_attributes[i]; + if (attr & Attribute::ByVal) { + const TypeRewriteRule* rule = + MatchRewriteRulesPointee(t, ByvalRewriteRules); + if (rule != 0 && RegUseForRewriteRule(rule) <= available) { + DEBUG(dbgs() << "REWRITING BYVAL " + << *t << " arg " << arg->getName() << " " << rule->name << "\n"); + FixFunctionByvalsParameter(F, + new_arguments, + new_attributes, + arg, + rule); + available -= RegUseForRewriteRule(rule); + continue; + } + } + + // fall through case - no rewrite is happening + new_arguments.push_back(arg); + new_attributes.push_back(attr); + available -= RegUseForType(t); + } + UpdateFunctionSignature(F, new_result_type, new_arguments, new_attributes); DEBUG(dbgs() << "FUNCTION AFTER "); @@ -539,31 +676,38 @@ void NaClCcRewrite::RewriteFunctionPrologAndEpilog(Function& F) { } // used for T in {CallInst, InvokeInst} +// TODO(robertm): try unifying this code with FunctionNeedsRewrite() template<class T> bool CallNeedsRewrite( const Instruction* inst, const TypeRewriteRule* ByvalRewriteRules, - const TypeRewriteRule* SretRewriteRules) { + const TypeRewriteRule* SretRewriteRules, + RegUse available) { const T* call = cast<T>(inst); // skip non parameter operands at the end - size_t num_params = call->getNumOperands() - - (isa<CallInst>(inst) ? 1 : 3); + size_t num_params = call->getNumOperands() - (isa<CallInst>(inst) ? 1 : 3); for (size_t i = 0; i < num_params; ++i) { Type* t = call->getOperand(i)->getType(); // byval and srets are modelled as pointers (to structs) - if (!t->isPointerTy()) continue; - Type* pointee = dyn_cast<PointerType>(t)->getElementType(); - - // param zero is for the return value - if (ByvalRewriteRules && call->paramHasAttr(i + 1, Attribute::ByVal)) { - if (0 != MatchRewriteRules(pointee, ByvalRewriteRules)) return true; - } - - if (SretRewriteRules && call->paramHasAttr(i + 1, Attribute::StructRet)) { - if (0 != MatchRewriteRules(pointee, SretRewriteRules)) return true; + if (t->isPointerTy()) { + Type* pointee = dyn_cast<PointerType>(t)->getElementType(); + + // param zero is for the return value + if (ByvalRewriteRules && call->paramHasAttr(i + 1, Attribute::ByVal)) { + const TypeRewriteRule* rule = + MatchRewriteRules(pointee, ByvalRewriteRules); + if (rule != 0 && RegUseForRewriteRule(rule) <= available) { + return true; + } + } else if (SretRewriteRules && + call->paramHasAttr(i + 1, Attribute::StructRet)) { + if (0 != MatchRewriteRules(pointee, SretRewriteRules)) { + return true; + } + } } + available -= RegUseForType(t); } - return false; } @@ -571,11 +715,11 @@ template<class T> bool CallNeedsRewrite( // which will then be used as argument when we rewrite the actual call // instruction. void PrependCompensationForByvals(std::vector<Value*>& new_operands, - std::vector<Attributes>& new_attributes, - Instruction* call, - Value* byval, - const TypeRewriteRule* rule, - LLVMContext& C) { + std::vector<Attributes>& new_attributes, + Instruction* call, + Value* byval, + const TypeRewriteRule* rule, + LLVMContext& C) { // convert byval poiner to char pointer Value* base = CastInst::CreatePointerCast( byval, PointerType::getInt8PtrTy(C), "byval_base", call); @@ -607,7 +751,21 @@ void CallsiteFixupSrets(Instruction* call, Type* new_type, const TypeRewriteRule* rule) { const char* pattern = rule->dst; - Instruction* next= call->getNextNode(); + Instruction* next; + if (isa<CallInst>(call)) { + next = call->getNextNode(); + } else if (isa<InvokeInst>(call)) { + // if this scheme turns out to be too simplistic (i.e. asserts fire) + // we need to introduce a new basic block for the compensation code. + BasicBlock* normal = dyn_cast<InvokeInst>(call)->getNormalDest(); + if (!normal->getSinglePredecessor()) { + llvm_unreachable("unexpected invoke normal bb"); + } + next = normal->getFirstNonPHI(); + } else { + llvm_unreachable("unexpected call instruction"); + } + if (next == 0) { llvm_unreachable("unexpected missing next instruction"); } @@ -702,11 +860,16 @@ void NaClCcRewrite::RewriteCallsite(Instruction* call, LLVMContext& C) { DEBUG(dbgs() << "CALLSITE BB BEFORE " << *BB); DEBUG(dbgs() << "\n"); DEBUG(dbgs() << *call << "\n"); + if (isa<InvokeInst>(call)) { + DEBUG(dbgs() << "\n" << *(dyn_cast<InvokeInst>(call)->getNormalDest())); + } // new_result(_type) is only relevent if an sret is rewritten // whish is indicated by sret_rule != 0 const TypeRewriteRule* sret_rule = 0; Type* new_result_type = call->getType(); + // This is the sret which was originally passed in as the first arg. + // After the rewrite we simply copy the function result into it. Value* new_result = 0; std::vector<Value*> old_operands; @@ -721,45 +884,51 @@ void NaClCcRewrite::RewriteCallsite(Instruction* call, LLVMContext& C) { llvm_unreachable("Unexpected instruction type"); } + // handle sret (just the book-keeping, 'new_result' is dealt with below) + // only the first arg can be "sret" + if (old_attributes[0] & Attribute::StructRet) { + sret_rule = MatchRewriteRulesPointee( + old_operands[0]->getType(), SretRewriteRules); + if (sret_rule) { + new_result_type = + GetNewReturnType(old_operands[0]->getType(), sret_rule, C); + new_result = old_operands[0]; + old_operands.erase(old_operands.begin()); + old_attributes.erase(old_attributes.begin()); + } + } + + // handle byval std::vector<Value*> new_operands; std::vector<Attributes> new_attributes; + RegUse available = AvailableRegs; for (size_t i = 0; i < old_operands.size(); ++i) { Value *operand = old_operands[i]; Type* t = operand->getType(); - const TypeRewriteRule* rule = 0; - if (old_attributes[i] & Attribute::ByVal) { - rule = MatchRewriteRulesPointee(t, ByvalRewriteRules); - } - if (rule == 0) { - new_operands.push_back(operand); - new_attributes.push_back(old_attributes[i]); - continue; - } + Attributes attr = old_attributes[i]; - DEBUG(dbgs() << "REWRITING BYVAL " - << *t << " arg " << i << " " << rule->name << "\n"); - PrependCompensationForByvals(new_operands, - new_attributes, - call, - operand, - rule, - C); - } - - // only the first arg can be "sret" - if (new_attributes[0] & Attribute::StructRet) { - sret_rule = MatchRewriteRulesPointee( - new_operands[0]->getType(), SretRewriteRules); - } + if (attr & Attribute::ByVal) { + const TypeRewriteRule* rule = + MatchRewriteRulesPointee(t, ByvalRewriteRules); + if (rule != 0 && RegUseForRewriteRule(rule) <= available) { + DEBUG(dbgs() << "REWRITING BYVAL " + << *t << " arg " << i << " " << rule->name << "\n"); + PrependCompensationForByvals(new_operands, + new_attributes, + call, + operand, + rule, + C); + available -= RegUseForRewriteRule(rule); + continue; + } + } - // we have to patch the call before we can add the sret compensation code - // because otherwise the type checker complains - if (sret_rule) { - new_result_type = GetNewReturnType(new_operands[0]->getType(), sret_rule, C); - new_result = new_operands[0]; - new_operands.erase(new_operands.begin()); - new_attributes.erase(new_attributes.begin()); + // fall through case - no rewrite is happening + new_operands.push_back(operand); + new_attributes.push_back(attr); + available -= RegUseForType(t); } // Note, this code is tricky. @@ -814,6 +983,9 @@ void NaClCcRewrite::RewriteCallsite(Instruction* call, LLVMContext& C) { DEBUG(dbgs() << "CALLSITE BB AFTER" << *BB); DEBUG(dbgs() << "\n"); DEBUG(dbgs() << *new_call << "\n"); + if (isa<InvokeInst>(call)) { + DEBUG(dbgs() << "\n" << *(dyn_cast<InvokeInst>(call)->getNormalDest())); + } } bool NaClCcRewrite::runOnFunction(Function &F) { @@ -822,7 +994,7 @@ bool NaClCcRewrite::runOnFunction(Function &F) { bool Changed = false; - if (FunctionNeedsRewrite(&F, ByvalRewriteRules, SretRewriteRules)) { + if (FunctionNeedsRewrite(&F, ByvalRewriteRules, SretRewriteRules, AvailableRegs)) { DEBUG(dbgs() << "FUNCTION NEEDS REWRITE " << F.getName() << "\n"); RewriteFunctionPrologAndEpilog(F); Changed = true; @@ -837,15 +1009,17 @@ bool NaClCcRewrite::runOnFunction(Function &F) { // we do decontructive magic below, so advance the iterator here // (this is still a little iffy) ++II; - if (isa<InvokeInst>(inst) || isa<CallInst>(inst)) { + // skip calls to llvm.dbg.declare, etc. + if (isa<IntrinsicInst>(inst)) continue; + if (isa<CallInst>(inst) && !CallNeedsRewrite<CallInst> - (inst, ByvalRewriteRules, SretRewriteRules)) continue; + (inst, ByvalRewriteRules, SretRewriteRules, AvailableRegs)) continue; if (isa<InvokeInst>(inst) && !CallNeedsRewrite<InvokeInst> - (inst, ByvalRewriteRules, SretRewriteRules)) continue; + (inst, ByvalRewriteRules, SretRewriteRules, AvailableRegs)) continue; RewriteCallsite(inst, F.getContext()); Changed = true; |