Add call instructions to Subzero's bitcode reader. BUG= https://code.google.com/p/nativeclient/issues/detail?id=3892 R=jvoung@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/577353003
diff --git a/src/IceConverter.cpp b/src/IceConverter.cpp index 2db34e9..ec797c1 100644 --- a/src/IceConverter.cpp +++ b/src/IceConverter.cpp
@@ -537,7 +537,8 @@ // Not an intrinsic call. if (NewInst == NULL) { - NewInst = Ice::InstCall::create(Func, NumArgs, Dest, CallTarget); + NewInst = Ice::InstCall::create(Func, NumArgs, Dest, CallTarget, + Inst->isTailCall()); } for (unsigned i = 0; i < NumArgs; ++i) { NewInst->addArg(convertOperand(Inst, i)); @@ -574,26 +575,38 @@ void validateIntrinsicCall(const Ice::InstCall *Call, const Ice::Intrinsics::FullIntrinsicInfo *I) { - assert(I->NumTypes >= 1); - if (I->Signature[0] == Ice::IceType_void) { - if (Call->getDest() != NULL) { - report_fatal_error( - "Return value for intrinsic func w/ void return type."); - } - } else { - if (I->Signature[0] != Call->getDest()->getType()) { - report_fatal_error("Mismatched return types."); - } + Ice::SizeT ArgIndex = 0; + switch (I->validateCall(Call, ArgIndex)) { + default: + report_fatal_error("Unknown validation error for intrinsic call"); + break; + case Ice::Intrinsics::IsValidCall: + break; + case Ice::Intrinsics::BadReturnType: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call expects return type " << I->getReturnType() + << ". Found: " << Call->getReturnType(); + report_fatal_error(StrBuf.str()); + break; } - if (Call->getNumArgs() + 1 != I->NumTypes) { - std::cerr << "Call->getNumArgs() " << (int)Call->getNumArgs() - << " I->NumTypes " << (int)I->NumTypes << "\n"; - report_fatal_error("Mismatched # of args."); + case Ice::Intrinsics::WrongNumOfArgs: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call expects " << I->getNumArgs() + << ". Found: " << Call->getNumArgs(); + report_fatal_error(StrBuf.str()); + break; } - for (size_t i = 1; i < I->NumTypes; ++i) { - if (Call->getArg(i - 1)->getType() != I->Signature[i]) { - report_fatal_error("Mismatched argument type."); - } + case Ice::Intrinsics::WrongCallArgType: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call argument " << ArgIndex << " expects type " + << I->getArgType(ArgIndex) + << ". Found: " << Call->getArg(ArgIndex)->getType(); + report_fatal_error(StrBuf.str()); + break; + } } }
diff --git a/src/IceInst.cpp b/src/IceInst.cpp index b84266a..d6e0f59 100644 --- a/src/IceInst.cpp +++ b/src/IceInst.cpp
@@ -566,6 +566,12 @@ Str << "label %" << getTargetFalse()->getName(); } +Type InstCall::getReturnType() const { + if (Dest == NULL) + return IceType_void; + return Dest->getType(); +} + void InstCall::dump(const Cfg *Func) const { Ostream &Str = Func->getContext()->getStrDump(); if (getDest()) {
diff --git a/src/IceInst.h b/src/IceInst.h index 18c38dd..cf507e6 100644 --- a/src/IceInst.h +++ b/src/IceInst.h
@@ -296,32 +296,36 @@ class InstCall : public Inst { public: static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest, - Operand *CallTarget) { + Operand *CallTarget, bool HasTailCall) { // Set HasSideEffects to true so that the call instruction can't be // dead-code eliminated. IntrinsicCalls can override this if the // particular intrinsic is deletable and has no side-effects. const bool HasSideEffects = true; const InstKind Kind = Inst::Call; - return new (Func->allocateInst<InstCall>()) - InstCall(Func, NumArgs, Dest, CallTarget, HasSideEffects, Kind); + return new (Func->allocateInst<InstCall>()) InstCall( + Func, NumArgs, Dest, CallTarget, HasTailCall, HasSideEffects, Kind); } void addArg(Operand *Arg) { addSource(Arg); } Operand *getCallTarget() const { return getSrc(0); } Operand *getArg(SizeT I) const { return getSrc(I + 1); } SizeT getNumArgs() const { return getSrcSize() - 1; } + bool isTailcall() const { return HasTailCall; } virtual void dump(const Cfg *Func) const; static bool classof(const Inst *Inst) { return Inst->getKind() == Call; } + Type getReturnType() const; protected: InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget, - bool HasSideEff, InstKind Kind) - : Inst(Func, Kind, NumArgs + 1, Dest) { + bool HasTailCall, bool HasSideEff, InstKind Kind) + : Inst(Func, Kind, NumArgs + 1, Dest), + HasTailCall(HasTailCall) { HasSideEffects = HasSideEff; addSource(CallTarget); } virtual ~InstCall() {} private: + bool HasTailCall; InstCall(const InstCall &) LLVM_DELETED_FUNCTION; InstCall &operator=(const InstCall &) LLVM_DELETED_FUNCTION; }; @@ -475,7 +479,7 @@ private: InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info) - : InstCall(Func, NumArgs, Dest, CallTarget, Info.HasSideEffects, + : InstCall(Func, NumArgs, Dest, CallTarget, false, Info.HasSideEffects, Inst::IntrinsicCall), Info(Info) {} InstIntrinsicCall(const InstIntrinsicCall &) LLVM_DELETED_FUNCTION;
diff --git a/src/IceIntrinsics.cpp b/src/IceIntrinsics.cpp index e941ff9..757455f 100644 --- a/src/IceIntrinsics.cpp +++ b/src/IceIntrinsics.cpp
@@ -14,6 +14,7 @@ #include "IceCfg.h" #include "IceCfgNode.h" +#include "IceInst.h" #include "IceIntrinsics.h" #include "IceLiveness.h" #include "IceOperand.h" @@ -226,4 +227,33 @@ return Order == Intrinsics::MemoryOrderSequentiallyConsistent; } +Intrinsics::ValidateCallValue +Intrinsics::FullIntrinsicInfo::validateCall(const Ice::InstCall *Call, + SizeT &ArgIndex) const { + assert(NumTypes >= 1); + Variable *Result = Call->getDest(); + if (Result == NULL) { + if (Signature[0] != Ice::IceType_void) + return Intrinsics::BadReturnType; + } else if (Signature[0] != Result->getType()) { + return Intrinsics::BadReturnType; + } + if (Call->getNumArgs() + 1 != NumTypes) { + return Intrinsics::WrongNumOfArgs; + } + for (size_t i = 1; i < NumTypes; ++i) { + if (Call->getArg(i - 1)->getType() != Signature[i]) { + ArgIndex = i; + return Intrinsics::WrongCallArgType; + } + } + return Intrinsics::IsValidCall; +} + +Type Intrinsics::FullIntrinsicInfo::getArgType(SizeT Index) const { + assert(NumTypes > 1); + assert(Index + 1 < NumTypes); + return Signature[Index + 1]; +} + } // end of namespace Ice
diff --git a/src/IceIntrinsics.h b/src/IceIntrinsics.h index d984b4c..bd0f118 100644 --- a/src/IceIntrinsics.h +++ b/src/IceIntrinsics.h
@@ -19,6 +19,8 @@ namespace Ice { +class InstCall; + static const size_t kMaxIntrinsicParameters = 6; class Intrinsics { @@ -108,6 +110,14 @@ enum ReturnsTwice ReturnsTwice : 1; }; + // The types of validation values for FullIntrinsicInfo.validateCall. + enum ValidateCallValue { + IsValidCall, // Valid use of instrinsic call. + BadReturnType, // Return type invalid for intrinsic. + WrongNumOfArgs, // Wrong number of arguments for intrinsic. + WrongCallArgType, // Argument of wrong type. + }; + // The complete set of information about an intrinsic. struct FullIntrinsicInfo { struct IntrinsicInfo Info; // Information that CodeGen would care about. @@ -115,6 +125,27 @@ // Sanity check during parsing. Type Signature[kMaxIntrinsicParameters]; uint8_t NumTypes; + + // Validates that type signature of call matches intrinsic. + // If WrongArgumentType is returned, ArgIndex is set to corresponding + // argument index. + ValidateCallValue validateCall(const Ice::InstCall *Call, + SizeT &ArgIndex) const; + + // Returns the return type of the intrinsic. + Type getReturnType() const { + assert(NumTypes > 1); + return Signature[0]; + } + + // Returns number of arguments expected. + SizeT getNumArgs() const { + assert(NumTypes > 1); + return NumTypes - 1; + } + + // Returns type of Index-th argument. + Type getArgType(SizeT Index) const; }; // Find the information about a given intrinsic, based on function name.
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h index 4b87354..1b65b3f 100644 --- a/src/IceTargetLoweringX8632.h +++ b/src/IceTargetLoweringX8632.h
@@ -161,7 +161,7 @@ const Type FunctionPointerType = IceType_i32; Constant *CallTarget = Ctx->getConstantSym(FunctionPointerType, 0, Name, SuppressMangling); - InstCall *Call = InstCall::create(Func, MaxSrcs, Dest, CallTarget); + InstCall *Call = InstCall::create(Func, MaxSrcs, Dest, CallTarget, false); return Call; } static Type stackSlotType();
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp index 1353b07..e87e0aa 100644 --- a/src/PNaClTranslator.cpp +++ b/src/PNaClTranslator.cpp
@@ -63,6 +63,7 @@ NumFunctionBlocks(0), GlobalVarPlaceHolderType(convertToLLVMType(Ice::IceType_i8)) { Mod->setDataLayout(PNaClDataLayout); + setErrStream(Translator.getContext()->getStrDump()); } virtual ~TopLevelParser() {} @@ -146,6 +147,26 @@ return ValueIDValues[ID]; } + /// Returns the corresponding constant associated with a global value + /// (i.e. relocatable). + Ice::Constant *getOrCreateGlobalConstantByID(unsigned ID) { + // TODO(kschimpf): Can this be built when creating global initializers? + if (ID >= ValueIDConstants.size()) { + if (ID >= ValueIDValues.size()) + return NULL; + ValueIDConstants.resize(ValueIDValues.size()); + } + Ice::Constant *C = ValueIDConstants[ID]; + if (C != NULL) + return C; + Value *V = ValueIDValues[ID]; + assert(isa<GlobalValue>(V)); + C = getTranslator().getContext()->getConstantSym(getIcePointerType(), 0, + V->getName()); + ValueIDConstants[ID] = C; + return C; + } + /// Returns the number of function addresses (i.e. ID's) defined in /// the bitcode file. unsigned getNumFunctionIDs() const { return NumFunctionIds; } @@ -247,6 +268,8 @@ std::vector<Type *> TypeIDValues; // The (global) value IDs. std::vector<WeakVH> ValueIDValues; + // Relocatable constants associated with ValueIDValues. + std::vector<Ice::Constant *> ValueIDConstants; // The number of function IDs. unsigned NumFunctionIds; // The number of function blocks (processed so far). @@ -973,8 +996,7 @@ // Returns the value referenced by the given value Index. Ice::Operand *getOperand(uint32_t Index) { if (Index < CachedNumGlobalValueIDs) { - // TODO(kschimpf): Define implementation. - report_fatal_error("getOperand of global addresses not implemented"); + return Context->getOrCreateGlobalConstantByID(Index); } uint32_t LocalIndex = Index - CachedNumGlobalValueIDs; if (LocalIndex >= LocalOperands.size()) { @@ -1123,6 +1145,18 @@ // is not understood. void ReportInvalidBinopOpcode(unsigned Opcode, Ice::Type Ty); + // Returns true if the Str begins with Prefix. + bool isStringPrefix(Ice::IceString &Str, Ice::IceString &Prefix) { + const size_t PrefixSize = Prefix.size(); + if (Str.size() < PrefixSize) + return false; + for (size_t i = 0; i < PrefixSize; ++i) { + if (Str[i] != Prefix[i]) + return false; + } + return true; + } + // Takes the PNaCl bitcode binary operator Opcode, and the opcode // type Ty, and sets Op to the corresponding ICE binary // opcode. Returns true if able to convert, false otherwise. @@ -1834,6 +1868,143 @@ Ice::InstStore::create(Func, Value, Address, Alignment)); break; } + case naclbitc::FUNC_CODE_INST_CALL: + case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: { + // CALL: [cc, fnid, arg0, arg1...] + // CALL_INDIRECT: [cc, fn, returnty, args...] + // + // Note: The difference between CALL and CALL_INDIRECT is that + // CALL has an explicit function address, while the CALL_INDIRECT + // is just an address. For CALL, we can infer the return type by + // looking up the type signature associated with the function + // address. For CALL_INDIRECT we can only infer the type signature + // via argument types, and the corresponding return type stored in + // CALL_INDIRECT record. + Ice::SizeT ParamsStartIndex = 2; + if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { + if (!isValidRecordSizeAtLeast(2, "function block call")) + return; + } else { + if (!isValidRecordSizeAtLeast(3, "function block call indirect")) + return; + ParamsStartIndex = 3; + } + + // Extract call information. + uint64_t CCInfo = Values[0]; + CallingConv::ID CallingConv; + if (!naclbitc::DecodeCallingConv(CCInfo >> 1, CallingConv)) { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Function call calling convention value " << (CCInfo >> 1) + << " not understood."; + Error(StrBuf.str()); + return; + } + bool IsTailCall = static_cast<bool>(CCInfo & 1); + + // Extract out the called function and its return type. + uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex); + Ice::Operand *Callee = getOperand(CalleeIndex); + Ice::Type ReturnType = Ice::IceType_void; + const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = NULL; + if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { + Function *Fcn = + dyn_cast<Function>(Context->getGlobalValueByID(CalleeIndex)); + if (Fcn == NULL) { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Function call to non-function: " << *Callee; + Error(StrBuf.str()); + return; + } + + FunctionType *FcnTy = Fcn->getFunctionType(); + ReturnType = Context->convertToIceType(FcnTy->getReturnType()); + + // Check if this direct call is to an Intrinsic (starts with "llvm.") + static Ice::IceString LLVMPrefix("llvm."); + Ice::IceString Name = Fcn->getName(); + if (isStringPrefix(Name, LLVMPrefix)) { + Ice::IceString Suffix = Name.substr(LLVMPrefix.size()); + IntrinsicInfo = + getTranslator().getContext()->getIntrinsicsInfo().find(Suffix); + if (!IntrinsicInfo) { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Invalid PNaCl intrinsic call to " << Name; + Error(StrBuf.str()); + return; + } + } + } else { + ReturnType = Context->convertToIceType(Context->getTypeByID(Values[2])); + } + + // Create the call instruction. + Ice::Variable *Dest = + (ReturnType == Ice::IceType_void) ? NULL : getNextInstVar(ReturnType); + Ice::SizeT NumParams = Values.size() - ParamsStartIndex; + Ice::InstCall *Inst = NULL; + if (IntrinsicInfo) { + Inst = + Ice::InstIntrinsicCall::create(Func, NumParams, Dest, Callee, + IntrinsicInfo->Info); + } else { + Inst = Ice::InstCall::create(Func, NumParams, Dest, Callee, IsTailCall); + } + + // Add parameters. + for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) { + Inst->addArg( + getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex)); + } + + // If intrinsic call, validate call signature. + if (IntrinsicInfo) { + Ice::SizeT ArgIndex = 0; + switch (IntrinsicInfo->validateCall(Inst, ArgIndex)) { + default: + Error("Unknown validation error for intrinsic call"); + // TODO(kschimpf) Remove error recovery once implementation complete. + break; + case Ice::Intrinsics::IsValidCall: + break; + case Ice::Intrinsics::BadReturnType: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call expects return type " + << IntrinsicInfo->getReturnType() + << ". Found: " << Inst->getReturnType(); + Error(StrBuf.str()); + // TODO(kschimpf) Remove error recovery once implementation complete. + break; + } + case Ice::Intrinsics::WrongNumOfArgs: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call expects " << IntrinsicInfo->getNumArgs() + << ". Found: " << Inst->getNumArgs(); + Error(StrBuf.str()); + // TODO(kschimpf) Remove error recovery once implementation complete. + break; + } + case Ice::Intrinsics::WrongCallArgType: { + std::string Buffer; + raw_string_ostream StrBuf(Buffer); + StrBuf << "Intrinsic call argument " << ArgIndex << " expects type " + << IntrinsicInfo->getArgType(ArgIndex) + << ". Found: " << Inst->getArg(ArgIndex)->getType(); + Error(StrBuf.str()); + // TODO(kschimpf) Remove error recovery once implementation complete. + break; + } + } + } + + CurrentNode->appendInst(Inst); + return; + } case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: { // FORWARDTYPEREF: [opval, ty] if (!isValidRecordSize(2, "function block forward type ref")) @@ -1842,8 +2013,6 @@ Context->getTypeByID(Values[1])))); break; } - case naclbitc::FUNC_CODE_INST_CALL: - case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: default: // Generate error message! BlockParserBaseClass::ProcessRecord();