diff --git a/llvm/include/llvm/Analysis/DebugInfo.h b/llvm/include/llvm/Analysis/DebugInfo.h index 908379726e91..f398fe43e05b 100644 --- a/llvm/include/llvm/Analysis/DebugInfo.h +++ b/llvm/include/llvm/Analysis/DebugInfo.h @@ -321,19 +321,6 @@ namespace llvm { bool describes(const Function *F); }; - /// DIInlinedSubprogram - This is a wrapper for an inlined subprogram. - class DIInlinedSubprogram : public DIGlobal { - public: - explicit DIInlinedSubprogram(GlobalVariable *GV = 0); - DICompositeType getType() const { return getFieldAs(8); } - - /// Verify - Verify that an inlined subprogram descriptor is well formed. - bool Verify() const; - - /// dump - print inlined subprogram. - void dump() const; - }; - /// DIGlobalVariable - This is a wrapper for a global variable. class DIGlobalVariable : public DIGlobal { public: @@ -376,7 +363,6 @@ namespace llvm { public: explicit DIBlock(GlobalVariable *GV = 0); - DICompileUnit getCompileUnit() const{ return getFieldAs(1); } DIDescriptor getContext() const { return getDescriptorField(1); } }; diff --git a/llvm/include/llvm/CodeGen/DwarfWriter.h b/llvm/include/llvm/CodeGen/DwarfWriter.h index 952d9ea84cb9..facd5f6e6a5f 100644 --- a/llvm/include/llvm/CodeGen/DwarfWriter.h +++ b/llvm/include/llvm/CodeGen/DwarfWriter.h @@ -91,7 +91,7 @@ public: unsigned RecordRegionStart(GlobalVariable *V); /// RecordRegionEnd - Indicate the end of a region. - unsigned RecordRegionEnd(GlobalVariable *V, DISubprogram &SP); + unsigned RecordRegionEnd(GlobalVariable *V); /// getRecordSourceLineCount - Count source lines. unsigned getRecordSourceLineCount(); diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index ac2670a163ef..7c99325b2dd1 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -16,6 +16,7 @@ #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" #include "llvm/Function.h" +#include "llvm/GlobalVariable.h" #include "llvm/Instructions.h" #include "llvm/Intrinsics.h" #include "llvm/ADT/SmallVector.h" @@ -383,12 +384,43 @@ Constant *llvm::ConstantFoldInstOperands(unsigned Opcode, const Type *DestTy, // the int size is >= the ptr size. This requires knowing the width of a // pointer, so it can't be done in ConstantExpr::getCast. if (ConstantExpr *CE = dyn_cast(Ops[0])) { - if (TD && CE->getOpcode() == Instruction::PtrToInt && + if (TD && TD->getPointerSizeInBits() <= CE->getType()->getPrimitiveSizeInBits()) { - Constant *Input = CE->getOperand(0); - Constant *C = FoldBitCast(Input, DestTy, *TD); - return C ? C : ConstantExpr::getBitCast(Input, DestTy); + if (CE->getOpcode() == Instruction::PtrToInt) { + Constant *Input = CE->getOperand(0); + Constant *C = FoldBitCast(Input, DestTy, *TD); + return C ? C : ConstantExpr::getBitCast(Input, DestTy); + } + // If there's a constant offset added to the integer value before + // it is casted back to a pointer, see if the expression can be + // converted into a GEP. + if (CE->getOpcode() == Instruction::Add) + if (ConstantInt *L = dyn_cast(CE->getOperand(0))) + if (ConstantExpr *R = dyn_cast(CE->getOperand(1))) + if (R->getOpcode() == Instruction::PtrToInt) + if (GlobalVariable *GV = + dyn_cast(R->getOperand(0))) { + const PointerType *GVTy = cast(GV->getType()); + if (const ArrayType *AT = + dyn_cast(GVTy->getElementType())) { + const Type *ElTy = AT->getElementType(); + uint64_t PaddedSize = TD->getTypePaddedSize(ElTy); + APInt PSA(L->getValue().getBitWidth(), PaddedSize); + if (ElTy == cast(DestTy)->getElementType() && + L->getValue().urem(PSA) == 0) { + APInt ElemIdx = L->getValue().udiv(PSA); + if (ElemIdx.ult(APInt(ElemIdx.getBitWidth(), + AT->getNumElements()))) { + Constant *Index[] = { + Constant::getNullValue(CE->getType()), + ConstantInt::get(ElemIdx) + }; + return ConstantExpr::getGetElementPtr(GV, &Index[0], 2); + } + } + } + } } } return ConstantExpr::getCast(Opcode, Ops[0], DestTy); diff --git a/llvm/lib/Analysis/DebugInfo.cpp b/llvm/lib/Analysis/DebugInfo.cpp index 4e398f27c174..181f3e9eff19 100644 --- a/llvm/lib/Analysis/DebugInfo.cpp +++ b/llvm/lib/Analysis/DebugInfo.cpp @@ -59,9 +59,6 @@ bool DIDescriptor::ValidDebugInfo(Value *V, CodeGenOpt::Level OptLevel) { case DW_TAG_subprogram: assert(DISubprogram(GV).Verify() && "Invalid DebugInfo value"); break; - case DW_TAG_inlined_subroutine: - assert(DIInlinedSubprogram(GV).Verify() && "Invalid DebugInfo value"); - break; case DW_TAG_lexical_block: /// FIXME. This interfers with the quality of generated code when /// during optimization. @@ -149,8 +146,6 @@ DIBasicType::DIBasicType(GlobalVariable *GV) : DIType(GV, dwarf::DW_TAG_base_type) {} DISubprogram::DISubprogram(GlobalVariable *GV) : DIGlobal(GV, dwarf::DW_TAG_subprogram) {} -DIInlinedSubprogram::DIInlinedSubprogram(GlobalVariable *GV) - : DIGlobal(GV, dwarf::DW_TAG_inlined_subroutine) {} DIGlobalVariable::DIGlobalVariable(GlobalVariable *GV) : DIGlobal(GV, dwarf::DW_TAG_variable) {} DIBlock::DIBlock(GlobalVariable *GV) @@ -291,25 +286,6 @@ bool DISubprogram::Verify() const { return true; } -/// Verify - Verify that an inlined subprogram descriptor is well formed. -bool DIInlinedSubprogram::Verify() const { - if (isNull()) - return false; - - if (getContext().isNull()) - return false; - - DICompileUnit CU = getCompileUnit(); - if (!CU.Verify()) - return false; - - DICompositeType Ty = getType(); - if (!Ty.isNull() && !Ty.Verify()) - return false; - - return true; -} - /// Verify - Verify that a global variable descriptor is well formed. bool DIGlobalVariable::Verify() const { if (isNull()) @@ -1007,8 +983,7 @@ namespace llvm { /// dump - print descriptor. void DIDescriptor::dump() const { - cerr << "[" << dwarf::TagString(getTag()) << "] "; - cerr << std::hex << "[GV:" << GV << "]" << std::dec; + cerr << " [" << dwarf::TagString(getTag()) << "]\n"; } /// dump - print compile unit. @@ -1110,11 +1085,6 @@ void DISubprogram::dump() const { DIGlobal::dump(); } -/// dump - print subprogram. -void DIInlinedSubprogram::dump() const { - DIGlobal::dump(); -} - /// dump - print global variable. void DIGlobalVariable::dump() const { cerr << " ["; getGlobal()->dump(); cerr << "] "; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfWriter.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfWriter.cpp index 8cb97313e3be..cb53168b415f 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfWriter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfWriter.cpp @@ -1094,7 +1094,7 @@ public: /// class DbgVariable { DIVariable Var; // Variable Descriptor. - unsigned FrameIndex; // Variable frame index. + unsigned FrameIndex; // Variable frame index. public: DbgVariable(DIVariable V, unsigned I) : Var(V), FrameIndex(I) {} @@ -1280,32 +1280,14 @@ class DwarfDebug : public Dwarf { /// DbgInlinedScopeMap - Tracks inlined scopes in the current function. DenseMap > DbgInlinedScopeMap; - /// InlineInfo - Keep track of inlined functions and their location. This - /// information is used to populate debug_inlined section. + /// InlineInfo - Keep track of inlined functions and their location. + /// This information is used to populate debug_inlined section. DenseMap > InlineInfo; /// InlinedVariableScopes - Scopes information for the inlined subroutine /// variables. DenseMap InlinedVariableScopes; - /// AbstractInstanceRootMap - Map of abstract instance roots of inlined - /// functions. These are subroutine entries that contain a DW_AT_inline - /// attribute. - DenseMap AbstractInstanceRootMap; - - /// AbstractInstanceRootList - List of abstract instance roots of inlined - /// functions. These are subroutine entries that contain a DW_AT_inline - /// attribute. - SmallVector AbstractInstanceRootList; - - /// LexicalScopeToConcreteInstMap - Map a concrete instance's DIE to the - /// lexical scope it's in. - DenseMap LexicalScopeToConcreteInstMap; - - /// LexicalScopeStack - A stack of lexical scopes. The top one is the current - /// scope. - SmallVector LexicalScopeStack; - /// DebugTimer - Timer for the Dwarf debug writer. Timer *DebugTimer; @@ -1787,7 +1769,7 @@ private: if (Element.getTag() == dwarf::DW_TAG_subprogram) ElemDie = CreateSubprogramDIE(DW_Unit, DISubprogram(Element.getGV())); - else if (Element.getTag() == dwarf::DW_TAG_variable) // ?? + else if (Element.getTag() == dwarf::DW_TAG_variable) // ??? ElemDie = CreateGlobalVariableDIE(DW_Unit, DIGlobalVariable(Element.getGV())); else @@ -1976,7 +1958,6 @@ private: if (!SP.isDefinition()) { AddUInt(SPDie, DW_AT_declaration, DW_FORM_flag, 1); - // Add arguments. Do not add arguments for subprogram definition. They // will be handled through RecordVariable. if (SPTag == DW_TAG_subroutine_type) @@ -2049,13 +2030,6 @@ private: DbgScope *&Slot = DbgScopeMap[V]; if (Slot) return Slot; - // Don't create a new scope if we already created one for an inlined - // function. - DenseMap::iterator - II = AbstractInstanceRootMap.find(V); - if (II != AbstractInstanceRootMap.end()) - return LexicalScopeStack.back(); - DbgScope *Parent = NULL; DIBlock Block(V); @@ -2076,19 +2050,31 @@ private: return Slot; } + /// createInlinedSubroutineScope - Returns the scope associated with the + /// inlined subroutine. + /// + DbgScope *createInlinedSubroutineScope(DISubprogram SP, unsigned Src, + unsigned Line, unsigned Col) { + DbgScope *Scope = + new DbgInlinedSubroutineScope(NULL, SP, Src, Line, Col); + + // FIXME - Add inlined function scopes to the root so we can delete them + // later. + assert (FunctionDbgScope && "Function scope info missing!"); + FunctionDbgScope->AddScope(Scope); + return Scope; + } + /// ConstructDbgScope - Construct the components of a scope. /// void ConstructDbgScope(DbgScope *ParentScope, unsigned ParentStartID, unsigned ParentEndID, DIE *ParentDie, CompileUnit *Unit) { - if (LexicalScopeToConcreteInstMap.find(ParentScope) == - LexicalScopeToConcreteInstMap.end()) { - // Add variables to scope. - SmallVector &Variables = ParentScope->getVariables(); - for (unsigned i = 0, N = Variables.size(); i < N; ++i) { - DIE *VariableDie = NewDbgScopeVariable(Variables[i], Unit); - if (VariableDie) ParentDie->AddChild(VariableDie); - } + // Add variables to scope. + SmallVector &Variables = ParentScope->getVariables(); + for (unsigned i = 0, N = Variables.size(); i < N; ++i) { + DIE *VariableDie = NewDbgScopeVariable(Variables[i], Unit); + if (VariableDie) ParentDie->AddChild(VariableDie); } // Add nested scopes. @@ -2113,14 +2099,17 @@ private: ConstructDbgScope(Scope, ParentStartID, ParentEndID, ParentDie, Unit); } else { DIE *ScopeDie = NULL; - - DenseMap::iterator I = - LexicalScopeToConcreteInstMap.find(Scope); - - if (I != LexicalScopeToConcreteInstMap.end()) - ScopeDie = I->second; - else + if (MainCU && TAI->doesDwarfUsesInlineInfoSection() + && Scope->isInlinedSubroutine()) { + ScopeDie = new DIE(DW_TAG_inlined_subroutine); + DIE *Origin = MainCU->getDieMapSlotFor(Scope->getDesc().getGV()); + AddDIEntry(ScopeDie, DW_AT_abstract_origin, DW_FORM_ref4, Origin); + AddUInt(ScopeDie, DW_AT_call_file, 0, Scope->getFile()); + AddUInt(ScopeDie, DW_AT_call_line, 0, Scope->getLine()); + AddUInt(ScopeDie, DW_AT_call_column, 0, Scope->getColumn()); + } else { ScopeDie = new DIE(DW_TAG_lexical_block); + } // Add the scope bounds. if (StartID) @@ -2176,29 +2165,6 @@ private: ConstructDbgScope(RootScope, 0, 0, SPDie, Unit); } - void ConstructAbstractDbgScope(DbgScope *AbsScope) { - // Exit if there is no root scope. - if (!AbsScope) return; - - DIDescriptor Desc = AbsScope->getDesc(); - if (Desc.isNull()) - return; - - // Get the subprogram debug information entry. - DISubprogram SPD(Desc.getGV()); - - // Get the compile unit context. - CompileUnit *Unit = MainCU; - if (!Unit) - Unit = &FindCompileUnit(SPD.getCompileUnit()); - - // Get the subprogram die. - DIE *SPDie = Unit->getDieMapSlotFor(SPD.getGV()); - assert(SPDie && "Missing subprogram descriptor"); - - ConstructDbgScope(AbsScope, 0, 0, SPDie, Unit); - } - /// ConstructDefaultDbgScope - Construct a default scope for the subprogram. /// void ConstructDefaultDbgScope(MachineFunction *MF) { @@ -2925,6 +2891,7 @@ private: DISubprogram SP(GV); std::string Name; std::string LName; + SP.getLinkageName(LName); SP.getName(Name); @@ -3120,10 +3087,8 @@ private: // Add to map. Slot = SubprogramDie; - // Add to context owner. Unit->getDie()->AddChild(SubprogramDie); - // Expose as global. std::string Name; Unit->AddGlobal(SP.getName(Name), SubprogramDie); @@ -3171,11 +3136,6 @@ public: for (unsigned j = 0, M = Values.size(); j < M; ++j) delete Values[j]; - for (DenseMap::iterator - I = AbstractInstanceRootMap.begin(), - E = AbstractInstanceRootMap.end(); I != E;++I) - delete I->second; - delete DebugTimer; } @@ -3382,12 +3342,6 @@ public: // information) needs to be explored. ConstructDefaultDbgScope(MF); - // Construct the DbgScope for abstract instances. - for (SmallVector::iterator - I = AbstractInstanceRootList.begin(), - E = AbstractInstanceRootList.end(); I != E; ++I) - ConstructAbstractDbgScope(*I); - DebugFrames.push_back(FunctionDebugFrameInfo(SubprogramCount, MMI->getFrameMoves())); @@ -3398,9 +3352,6 @@ public: DbgInlinedScopeMap.clear(); InlinedVariableScopes.clear(); FunctionDbgScope = NULL; - LexicalScopeStack.clear(); - AbstractInstanceRootList.clear(); - LexicalScopeToConcreteInstMap.clear(); } Lines.clear(); @@ -3478,7 +3429,6 @@ public: DbgScope *Scope = getOrCreateScope(V); unsigned ID = MMI->NextLabelID(); if (!Scope->getStartLabelID()) Scope->setStartLabelID(ID); - LexicalScopeStack.push_back(Scope); if (TimePassesIsEnabled) DebugTimer->stopTimer(); @@ -3487,14 +3437,13 @@ public: } /// RecordRegionEnd - Indicate the end of a region. - unsigned RecordRegionEnd(GlobalVariable *V, DISubprogram &SP) { + unsigned RecordRegionEnd(GlobalVariable *V) { if (TimePassesIsEnabled) DebugTimer->startTimer(); - unsigned ID = MMI->NextLabelID(); DbgScope *Scope = getOrCreateScope(V); + unsigned ID = MMI->NextLabelID(); Scope->setEndLabelID(ID); - LexicalScopeStack.pop_back(); if (TimePassesIsEnabled) DebugTimer->stopTimer(); @@ -3548,59 +3497,28 @@ public: if (TimePassesIsEnabled) DebugTimer->startTimer(); + std::string Dir, Fn; + unsigned Src = GetOrCreateSourceID(CU.getDirectory(Dir), + CU.getFilename(Fn)); + DbgScope *Scope = createInlinedSubroutineScope(SP, Src, Line, Col); + Scope->setStartLabelID(LabelID); + MMI->RecordUsedDbgLabel(LabelID); GlobalVariable *GV = SP.getGV(); - DenseMap::iterator - II = AbstractInstanceRootMap.find(GV); - if (II == AbstractInstanceRootMap.end()) { - // Create an abstract instance entry for this inlined function if it - // doesn't already exist. - DbgScope *Scope = new DbgScope(NULL, DIDescriptor(GV)); + DenseMap >::iterator + SI = DbgInlinedScopeMap.find(GV); - // Get the compile unit context. - CompileUnit *Unit = &FindCompileUnit(SP.getCompileUnit()); - DIE *SPDie = Unit->getDieMapSlotFor(GV); - assert(SPDie && "Missing subprogram descriptor!"); + if (SI == DbgInlinedScopeMap.end()) + DbgInlinedScopeMap[GV].push_back(Scope); + else + SI->second.push_back(Scope); - // Mark as being inlined. This makes this subprogram entry an abstract - // instance root. - // FIXME: Our debugger doesn't care about the value of DW_AT_inline, only - // that it's defined. It probably won't change in the future, but this - // could be more elegant. - AddUInt(SPDie, DW_AT_inline, 0, DW_INL_declared_not_inlined); - - // Keep track of the scope that's inlined into this function. - DenseMap >::iterator - SI = DbgInlinedScopeMap.find(GV); - - if (SI == DbgInlinedScopeMap.end()) - DbgInlinedScopeMap[GV].push_back(Scope); - else - SI->second.push_back(Scope); - - // Track the start label for this inlined function. - DenseMap >::iterator - I = InlineInfo.find(GV); - - if (I == InlineInfo.end()) - InlineInfo[GV].push_back(LabelID); - else - I->second.push_back(LabelID); - - AbstractInstanceRootMap[GV] = Scope; - AbstractInstanceRootList.push_back(Scope); - } - - // Create a concrete inlined instance for this inlined function. - DIE *ScopeDie = new DIE(DW_TAG_inlined_subroutine); - CompileUnit *Unit = &FindCompileUnit(SP.getCompileUnit()); - DIE *Origin = Unit->getDieMapSlotFor(GV); - AddDIEntry(ScopeDie, DW_AT_abstract_origin, DW_FORM_ref4, Origin); - AddUInt(ScopeDie, DW_AT_call_file, 0, Unit->getID()); - AddUInt(ScopeDie, DW_AT_call_line, 0, Line); - AddUInt(ScopeDie, DW_AT_call_column, 0, Col); - - LexicalScopeToConcreteInstMap[LexicalScopeStack.back()] = ScopeDie; + DenseMap >::iterator + I = InlineInfo.find(GV); + if (I == InlineInfo.end()) + InlineInfo[GV].push_back(LabelID); + else + I->second.push_back(LabelID); if (TimePassesIsEnabled) DebugTimer->stopTimer(); @@ -3610,7 +3528,6 @@ public: /// RecordInlinedFnEnd - Indicate the end of inlined subroutine. unsigned RecordInlinedFnEnd(DISubprogram &SP) { - // FIXME: This function never seems to be called!! if (!TAI->doesDwarfUsesInlineInfoSection()) return 0; @@ -4812,8 +4729,8 @@ unsigned DwarfWriter::RecordRegionStart(GlobalVariable *V) { } /// RecordRegionEnd - Indicate the end of a region. -unsigned DwarfWriter::RecordRegionEnd(GlobalVariable *V, DISubprogram &SP) { - return DD->RecordRegionEnd(V, SP); +unsigned DwarfWriter::RecordRegionEnd(GlobalVariable *V) { + return DD->RecordRegionEnd(V); } /// getRecordSourceLineCount - Count source lines. diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index ec7aeb07655f..22051867a001 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -365,8 +365,7 @@ bool FastISel::SelectCall(User *I) { BuildMI(MBB, DL, II).addImm(ID); } else { const TargetInstrDesc &II = TII.get(TargetInstrInfo::DBG_LABEL); - ID = DW->RecordRegionEnd(cast(REI->getContext()), - Subprogram); + ID = DW->RecordRegionEnd(cast(REI->getContext())); BuildMI(MBB, DL, II).addImm(ID); } } @@ -392,7 +391,6 @@ bool FastISel::SelectCall(User *I) { // FIXME : Why DebugLoc is reset at the beginning of each block ? if (PrevLoc.isUnknown()) return true; - // Record the source line. unsigned Line = Subprogram.getLineNumber(); setCurDebugLoc(DebugLoc::get(MF.getOrCreateDebugLocID( @@ -412,10 +410,10 @@ bool FastISel::SelectCall(User *I) { unsigned Line = Subprogram.getLineNumber(); MF.setDefaultDebugLoc(DebugLoc::get(MF.getOrCreateDebugLocID( CompileUnit.getGV(), Line, 0))); - - if (DW && DW->ShouldEmitDwarfDebug()) + if (DW && DW->ShouldEmitDwarfDebug()) { // llvm.dbg.func_start also defines beginning of function scope. DW->RecordRegionStart(cast(FSI->getSubprogram())); + } } return true; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp index cf5dd9f23760..fd89108392ce 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp @@ -3944,8 +3944,7 @@ SelectionDAGLowering::visitIntrinsicCall(CallInst &I, unsigned Intrinsic) { } unsigned LabelID = - DW->RecordRegionEnd(cast(REI.getContext()), - Subprogram); + DW->RecordRegionEnd(cast(REI.getContext())); DAG.setRoot(DAG.getLabel(ISD::DBG_LABEL, getCurDebugLoc(), getRoot(), LabelID)); } diff --git a/llvm/test/Transforms/InstCombine/constant-fold-ptr-casts.ll b/llvm/test/Transforms/InstCombine/constant-fold-ptr-casts.ll new file mode 100644 index 000000000000..27c460689184 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/constant-fold-ptr-casts.ll @@ -0,0 +1,18 @@ +; RUN: llvm-as < %s | opt -instcombine | llvm-dis | grep {ret i32 2143034560} + +; Instcombine should be able to completely fold this code. + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128" +target triple = "i686-apple-darwin8" + +@bar = constant [3 x i64] [i64 9220983451228067448, i64 9220983451228067449, i64 9220983450959631991], align 8 + +define i32 @foo() nounwind { +entry: + %tmp87.2 = load i64* inttoptr (i32 add (i32 16, i32 ptrtoint ([3 x i64]* @bar to i32)) to i64*), align 8 + %t0 = bitcast i64 %tmp87.2 to double + %tmp9192.2 = fptrunc double %t0 to float + %t1 = bitcast float %tmp9192.2 to i32 + ret i32 %t1 +} +