Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -40,6 +40,11 @@ functionality, or simply have a lot to talk about), see the `NOTE` below for adding a new subsection. +* The optimizer will now convert calls to memcmp into a calls to bcmp in some + circumstances. Users who are building freestanding code (not depending on the + platform's libc) without specifying -ffreestanding may need to either pass + -fno-builtin-bcmp, or provide a bcmp function. + .. NOTE If you would like to document a larger change, then you can add a subsection about it right here. You can copy the following boilerplate Index: include/llvm/Transforms/Utils/BuildLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/BuildLibCalls.h +++ include/llvm/Transforms/Utils/BuildLibCalls.h @@ -92,6 +92,10 @@ Value *emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, const DataLayout &DL, const TargetLibraryInfo *TLI); + /// Emit a call to the bcmp function. + Value *emitBCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, + const DataLayout &DL, const TargetLibraryInfo *TLI); + /// Emit a call to the unary function named 'Name' (e.g. 'floor'). This /// function is known to take a single of type matching 'Op' and returns one /// value with the same type. If 'Op' is a long double, 'l' is added as the Index: lib/Analysis/InstructionPrecedenceTracking.cpp =================================================================== --- lib/Analysis/InstructionPrecedenceTracking.cpp +++ lib/Analysis/InstructionPrecedenceTracking.cpp @@ -19,6 +19,7 @@ #include "llvm/Analysis/InstructionPrecedenceTracking.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/PatternMatch.h" using namespace llvm; @@ -152,5 +153,8 @@ bool MemoryWriteTracking::isSpecialInstruction( const Instruction *Insn) const { + using namespace PatternMatch; + if (match(Insn, m_Intrinsic())) + return false; return Insn->mayWriteToMemory(); } Index: lib/Analysis/TargetLibraryInfo.cpp =================================================================== --- lib/Analysis/TargetLibraryInfo.cpp +++ lib/Analysis/TargetLibraryInfo.cpp @@ -49,6 +49,16 @@ return true; } +static bool hasBcmp(const Triple &TT) { + // Posix removed support from bcmp() in 2001, but the glibc and several + // implementations of the libc still have it. + if (TT.isOSLinux()) + return TT.isGNUEnvironment() || TT.isMusl(); + // Both NetBSD and OpenBSD are planning to remove the function. Windows does + // not have it. + return TT.isOSFreeBSD() || TT.isOSSolaris() || TT.isOSDarwin(); +} + /// Initialize the set of available library functions based on the specified /// target triple. This should be carefully written so that a missing target /// triple gets a sane set of defaults. @@ -141,6 +151,9 @@ TLI.setUnavailable(LibFunc_sincospif_stret); } + if (!hasBcmp(T)) + TLI.setUnavailable(LibFunc_bcmp); + if (T.isMacOSX() && T.getArch() == Triple::x86 && !T.isMacOSXVersionLT(10, 7)) { // x86-32 OSX has a scheme where fwrite and fputs (and some other functions Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -4347,7 +4347,8 @@ // is guaranteed to return. return CS.onlyReadsMemory() || CS.onlyAccessesArgMemory() || match(I, m_Intrinsic()) || - match(I, m_Intrinsic()); + match(I, m_Intrinsic()) || + match(I, m_Intrinsic()); } // Other instructions return normally. Index: lib/CodeGen/GlobalISel/LegalizerHelper.cpp =================================================================== --- lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -1176,7 +1176,7 @@ } case TargetOpcode::G_BRCOND: Observer.changingInstr(MI); - widenScalarSrc(MI, WideTy, 0, TargetOpcode::G_ANYEXT); + widenScalarSrc(MI, WideTy, 0, MIRBuilder.getBoolExtOp(false, false)); Observer.changedInstr(MI); return Legalized; Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5661,7 +5661,7 @@ if (!V) return nullptr; - if (handleDebugValue(V, Variable, Expression, DI.getDebugLoc(), dl, + if (handleDebugValue(V, Variable, Expression, dl, DI.getDebugLoc(), SDNodeOrder)) return nullptr; Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -1128,26 +1128,27 @@ Error TempFile::discard() { Done = true; - std::error_code RemoveEC; -// On windows closing will remove the file. -#ifndef _WIN32 - // Always try to close and remove. - if (!TmpName.empty()) { - RemoveEC = fs::remove(TmpName); - sys::DontRemoveFileOnSignal(TmpName); - } -#endif - - if (!RemoveEC) - TmpName = ""; - if (FD != -1 && close(FD) == -1) { std::error_code EC = std::error_code(errno, std::generic_category()); return errorCodeToError(EC); } FD = -1; +#ifdef _WIN32 + // On windows closing will remove the file. + TmpName = ""; + return Error::success(); +#else + // Always try to close and remove. + std::error_code RemoveEC; + if (!TmpName.empty()) { + RemoveEC = fs::remove(TmpName); + sys::DontRemoveFileOnSignal(TmpName); + if (!RemoveEC) + TmpName = ""; + } return errorCodeToError(RemoveEC); +#endif } Error TempFile::keep(const Twine &Name) { Index: lib/Target/ARM/ARMBaseInstrInfo.cpp =================================================================== --- lib/Target/ARM/ARMBaseInstrInfo.cpp +++ lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -2825,8 +2825,11 @@ if (!MI && !SubAdd) return false; - // The single candidate is called MI. - if (!MI) MI = SubAdd; + // If we found a SubAdd, use it as it will be closer to the CMP + if (SubAdd) { + MI = SubAdd; + IsThumb1 = false; + } // We can't use a predicated instruction - it doesn't always write the flags. if (isPredicated(*MI)) Index: lib/Target/Mips/MipsInstructionSelector.cpp =================================================================== --- lib/Target/Mips/MipsInstructionSelector.cpp +++ lib/Target/Mips/MipsInstructionSelector.cpp @@ -153,6 +153,13 @@ .addImm(0); break; } + case G_BRCOND: { + MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::BNE)) + .add(I.getOperand(0)) + .addUse(Mips::ZERO) + .add(I.getOperand(1)); + break; + } case G_STORE: case G_LOAD: case G_ZEXTLOAD: Index: lib/Target/Mips/MipsLegalizerInfo.cpp =================================================================== --- lib/Target/Mips/MipsLegalizerInfo.cpp +++ lib/Target/Mips/MipsLegalizerInfo.cpp @@ -52,6 +52,10 @@ .minScalar(0, s32) .minScalar(1, s32); + getActionDefinitionsBuilder(G_BRCOND) + .legalFor({s32}) + .minScalar(0, s32); + getActionDefinitionsBuilder({G_AND, G_OR, G_XOR}) .legalFor({s32}) .clampScalar(0, s32, s32); Index: lib/Target/Mips/MipsRegisterBankInfo.cpp =================================================================== --- lib/Target/Mips/MipsRegisterBankInfo.cpp +++ lib/Target/Mips/MipsRegisterBankInfo.cpp @@ -106,6 +106,7 @@ case G_CONSTANT: case G_FRAME_INDEX: case G_GLOBAL_VALUE: + case G_BRCOND: OperandsMapping = getOperandsMapping({&Mips::ValueMappings[Mips::GPRIdx], nullptr}); break; Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1170,7 +1170,7 @@ INITIALIZE_PASS(ASanGlobalsMetadataWrapperPass, "asan-globals-md", "Read metadata to mark which globals should be instrumented " "when running ASan.", - false, true); + false, true) char AddressSanitizerLegacyPass::ID = 0; Index: lib/Transforms/Utils/BuildLibCalls.cpp =================================================================== --- lib/Transforms/Utils/BuildLibCalls.cpp +++ lib/Transforms/Utils/BuildLibCalls.cpp @@ -930,28 +930,41 @@ return CI; } -Value *llvm::emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, - const DataLayout &DL, const TargetLibraryInfo *TLI) { - if (!TLI->has(LibFunc_memcmp)) +// Common code for memcmp() and bcmp(), which have the exact same properties, +// just a slight difference in semantics. +static Value *emitMemCmpOrBcmp(llvm::LibFunc libfunc, Value *Ptr1, Value *Ptr2, + Value *Len, IRBuilder<> &B, const DataLayout &DL, + const TargetLibraryInfo *TLI) { + if (!TLI->has(libfunc)) return nullptr; Module *M = B.GetInsertBlock()->getModule(); - StringRef MemCmpName = TLI->getName(LibFunc_memcmp); + StringRef CmpFnName = TLI->getName(libfunc); LLVMContext &Context = B.GetInsertBlock()->getContext(); - FunctionCallee MemCmp = - M->getOrInsertFunction(MemCmpName, B.getInt32Ty(), B.getInt8PtrTy(), + FunctionCallee CmpFn = + M->getOrInsertFunction(CmpFnName, B.getInt32Ty(), B.getInt8PtrTy(), B.getInt8PtrTy(), DL.getIntPtrType(Context)); - inferLibFuncAttributes(M, MemCmpName, *TLI); + inferLibFuncAttributes(M, CmpFnName, *TLI); CallInst *CI = B.CreateCall( - MemCmp, {castToCStr(Ptr1, B), castToCStr(Ptr2, B), Len}, MemCmpName); + CmpFn, {castToCStr(Ptr1, B), castToCStr(Ptr2, B), Len}, CmpFnName); if (const Function *F = - dyn_cast(MemCmp.getCallee()->stripPointerCasts())) + dyn_cast(CmpFn.getCallee()->stripPointerCasts())) CI->setCallingConv(F->getCallingConv()); return CI; } +Value *llvm::emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, + const DataLayout &DL, const TargetLibraryInfo *TLI) { + return emitMemCmpOrBcmp(LibFunc_memcmp, Ptr1, Ptr2, Len, B, DL, TLI); +} + +Value *llvm::emitBCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, + const DataLayout &DL, const TargetLibraryInfo *TLI) { + return emitMemCmpOrBcmp(LibFunc_bcmp, Ptr1, Ptr2, Len, B, DL, TLI); +} + /// Append a suffix to the function name according to the type of 'Op'. static void appendTypeSuffix(Value *Op, StringRef &Name, SmallString<20> &NameBuffer) { Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -831,18 +831,9 @@ return B.CreateGEP(B.getInt8Ty(), SrcStr, B.getInt64(I), "memchr"); } -Value *LibCallSimplifier::optimizeMemCmp(CallInst *CI, IRBuilder<> &B) { - Value *LHS = CI->getArgOperand(0), *RHS = CI->getArgOperand(1); - - if (LHS == RHS) // memcmp(s,s,x) -> 0 - return Constant::getNullValue(CI->getType()); - - // Make sure we have a constant length. - ConstantInt *LenC = dyn_cast(CI->getArgOperand(2)); - if (!LenC) - return nullptr; - - uint64_t Len = LenC->getZExtValue(); +static Value *optimizeMemCmpConstantSize(CallInst *CI, Value *LHS, Value *RHS, + uint64_t Len, IRBuilder<> &B, + const DataLayout &DL) { if (Len == 0) // memcmp(s1,s2,0) -> 0 return Constant::getNullValue(CI->getType()); @@ -912,6 +903,28 @@ Ret = 1; return ConstantInt::get(CI->getType(), Ret); } + return nullptr; +} + +Value *LibCallSimplifier::optimizeMemCmp(CallInst *CI, IRBuilder<> &B) { + Value *LHS = CI->getArgOperand(0), *RHS = CI->getArgOperand(1); + Value *Size = CI->getArgOperand(2); + + if (LHS == RHS) // memcmp(s,s,x) -> 0 + return Constant::getNullValue(CI->getType()); + + // Handle constant lengths. + if (ConstantInt *LenC = dyn_cast(Size)) + if (Value *Res = optimizeMemCmpConstantSize(CI, LHS, RHS, + LenC->getZExtValue(), B, DL)) + return Res; + + // memcmp(x, y, Len) == 0 -> bcmp(x, y, Len) == 0 + // `bcmp` can be more efficient than memcmp because it only has to know that + // there is a difference, not where is is. + if (isOnlyUsedInZeroEqualityComparison(CI) && TLI->has(LibFunc_bcmp)) { + return emitBCmp(LHS, RHS, Size, B, DL, TLI); + } return nullptr; } @@ -1137,10 +1150,10 @@ IRBuilder<> &B) { if (!isa(Call)) return nullptr; - + IRBuilder<>::FastMathFlagGuard Guard(B); B.setFastMathFlags(Call->getFastMathFlags()); - + // TODO: Can this be shared to also handle LLVM intrinsics? Value *X; switch (Func) { Index: test/CodeGen/Mips/GlobalISel/instruction-select/branch.mir =================================================================== --- /dev/null +++ test/CodeGen/Mips/GlobalISel/instruction-select/branch.mir @@ -0,0 +1,105 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32 +--- | + + define i32 @Unconditional_branch(i32 %a, i32 %b) { + entry: + br label %block + + end: ; preds = %block + ret i32 %a + + block: ; preds = %entry + br label %end + } + + define i32 @Conditional_branch(i1 %cond, i32 %a, i32 %b) { + br i1 %cond, label %if.then, label %if.else + + if.then: ; preds = %0 + ret i32 %a + + if.else: ; preds = %0 + ret i32 %b + } + +... +--- +name: Unconditional_branch +alignment: 2 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Unconditional_branch + ; MIPS32: bb.0.entry: + ; MIPS32: successors: %bb.2(0x80000000) + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: J %bb.2, implicit-def $at + ; MIPS32: bb.1.end: + ; MIPS32: $v0 = COPY [[COPY]] + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.block: + ; MIPS32: successors: %bb.1(0x80000000) + ; MIPS32: J %bb.1, implicit-def $at + bb.1.entry: + liveins: $a0, $a1 + + %0:gprb(s32) = COPY $a0 + G_BR %bb.3 + + bb.2.end: + $v0 = COPY %0(s32) + RetRA implicit $v0 + + bb.3.block: + G_BR %bb.2 + +... +--- +name: Conditional_branch +alignment: 2 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Conditional_branch + ; MIPS32: bb.0 (%ir-block.0): + ; MIPS32: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; MIPS32: liveins: $a0, $a1, $a2 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[COPY2:%[0-9]+]]:gpr32 = COPY $a2 + ; MIPS32: [[LUi:%[0-9]+]]:gpr32 = LUi 0 + ; MIPS32: [[ORi:%[0-9]+]]:gpr32 = ORi [[LUi]], 1 + ; MIPS32: [[AND:%[0-9]+]]:gpr32 = AND [[COPY]], [[ORi]] + ; MIPS32: BNE [[AND]], $zero, %bb.1, implicit-def $at + ; MIPS32: J %bb.2, implicit-def $at + ; MIPS32: bb.1.if.then: + ; MIPS32: $v0 = COPY [[COPY1]] + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.if.else: + ; MIPS32: $v0 = COPY [[COPY2]] + ; MIPS32: RetRA implicit $v0 + bb.1 (%ir-block.0): + liveins: $a0, $a1, $a2 + + %3:gprb(s32) = COPY $a0 + %1:gprb(s32) = COPY $a1 + %2:gprb(s32) = COPY $a2 + %5:gprb(s32) = G_CONSTANT i32 1 + %6:gprb(s32) = COPY %3(s32) + %4:gprb(s32) = G_AND %6, %5 + G_BRCOND %4(s32), %bb.2 + G_BR %bb.3 + + bb.2.if.then: + $v0 = COPY %1(s32) + RetRA implicit $v0 + + bb.3.if.else: + $v0 = COPY %2(s32) + RetRA implicit $v0 + +... Index: test/CodeGen/Mips/GlobalISel/legalizer/branch.mir =================================================================== --- /dev/null +++ test/CodeGen/Mips/GlobalISel/legalizer/branch.mir @@ -0,0 +1,100 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32 +--- | + + define i32 @Unconditional_branch(i32 %a, i32 %b) { + entry: + br label %block + + end: ; preds = %block + ret i32 %a + + block: ; preds = %entry + br label %end + } + + define i32 @Conditional_branch(i1 %cond, i32 %a, i32 %b) { + br i1 %cond, label %if.then, label %if.else + + if.then: ; preds = %0 + ret i32 %a + + if.else: ; preds = %0 + ret i32 %b + } + +... +--- +name: Unconditional_branch +alignment: 2 +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Unconditional_branch + ; MIPS32: bb.0.entry: + ; MIPS32: successors: %bb.2(0x80000000) + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $a0 + ; MIPS32: G_BR %bb.2 + ; MIPS32: bb.1.end: + ; MIPS32: $v0 = COPY [[COPY]](s32) + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.block: + ; MIPS32: successors: %bb.1(0x80000000) + ; MIPS32: G_BR %bb.1 + bb.1.entry: + liveins: $a0, $a1 + + %0:_(s32) = COPY $a0 + G_BR %bb.3 + + bb.2.end: + $v0 = COPY %0(s32) + RetRA implicit $v0 + + bb.3.block: + G_BR %bb.2 + +... +--- +name: Conditional_branch +alignment: 2 +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Conditional_branch + ; MIPS32: bb.0 (%ir-block.0): + ; MIPS32: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; MIPS32: liveins: $a0, $a1, $a2 + ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:_(s32) = COPY $a1 + ; MIPS32: [[COPY2:%[0-9]+]]:_(s32) = COPY $a2 + ; MIPS32: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; MIPS32: [[COPY3:%[0-9]+]]:_(s32) = COPY [[COPY]](s32) + ; MIPS32: [[AND:%[0-9]+]]:_(s32) = G_AND [[COPY3]], [[C]] + ; MIPS32: G_BRCOND [[AND]](s32), %bb.1 + ; MIPS32: G_BR %bb.2 + ; MIPS32: bb.1.if.then: + ; MIPS32: $v0 = COPY [[COPY1]](s32) + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.if.else: + ; MIPS32: $v0 = COPY [[COPY2]](s32) + ; MIPS32: RetRA implicit $v0 + bb.1 (%ir-block.0): + liveins: $a0, $a1, $a2 + + %3:_(s32) = COPY $a0 + %0:_(s1) = G_TRUNC %3(s32) + %1:_(s32) = COPY $a1 + %2:_(s32) = COPY $a2 + G_BRCOND %0(s1), %bb.2 + G_BR %bb.3 + + bb.2.if.then: + $v0 = COPY %1(s32) + RetRA implicit $v0 + + bb.3.if.else: + $v0 = COPY %2(s32) + RetRA implicit $v0 + +... + Index: test/CodeGen/Mips/GlobalISel/llvm-ir/branch.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/GlobalISel/llvm-ir/branch.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O0 -mtriple=mipsel-linux-gnu -global-isel -verify-machineinstrs %s -o -| FileCheck %s -check-prefixes=MIPS32 +define i32 @Unconditional_branch(i32 %a, i32 %b) { +; MIPS32-LABEL: Unconditional_branch: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: addiu $sp, $sp, -8 +; MIPS32-NEXT: .cfi_def_cfa_offset 8 +; MIPS32-NEXT: sw $4, 4($sp) # 4-byte Folded Spill +; MIPS32-NEXT: j $BB0_2 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB0_1: # %end +; MIPS32-NEXT: lw $2, 4($sp) # 4-byte Folded Reload +; MIPS32-NEXT: addiu $sp, $sp, 8 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB0_2: # %block +; MIPS32-NEXT: j $BB0_1 +; MIPS32-NEXT: nop +entry: + br label %block + ret i32 %b +end: + ret i32 %a +block: + br label %end +} + +define i32 @Conditional_branch(i1 %cond, i32 %a, i32 %b) { +; MIPS32-LABEL: Conditional_branch: +; MIPS32: # %bb.0: +; MIPS32-NEXT: addiu $sp, $sp, -8 +; MIPS32-NEXT: .cfi_def_cfa_offset 8 +; MIPS32-NEXT: lui $1, 0 +; MIPS32-NEXT: ori $1, $1, 1 +; MIPS32-NEXT: and $1, $4, $1 +; MIPS32-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32-NEXT: bnez $1, $BB1_2 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.1: +; MIPS32-NEXT: j $BB1_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB1_2: # %if.then +; MIPS32-NEXT: lw $2, 4($sp) # 4-byte Folded Reload +; MIPS32-NEXT: addiu $sp, $sp, 8 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB1_3: # %if.else +; MIPS32-NEXT: lw $2, 0($sp) # 4-byte Folded Reload +; MIPS32-NEXT: addiu $sp, $sp, 8 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop + br i1 %cond, label %if.then, label %if.else +if.then: + ret i32 %a +if.else: + ret i32 %b +} Index: test/CodeGen/Mips/GlobalISel/regbankselect/branch.mir =================================================================== --- /dev/null +++ test/CodeGen/Mips/GlobalISel/regbankselect/branch.mir @@ -0,0 +1,103 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=regbankselect -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32 +--- | + + define i32 @Unconditional_branch(i32 %a, i32 %b) { + entry: + br label %block + + end: ; preds = %block + ret i32 %a + + block: ; preds = %entry + br label %end + } + + define i32 @Conditional_branch(i1 %cond, i32 %a, i32 %b) { + br i1 %cond, label %if.then, label %if.else + + if.then: ; preds = %0 + ret i32 %a + + if.else: ; preds = %0 + ret i32 %b + } + +... +--- +name: Unconditional_branch +alignment: 2 +legalized: true +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Unconditional_branch + ; MIPS32: bb.0.entry: + ; MIPS32: successors: %bb.2(0x80000000) + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gprb(s32) = COPY $a0 + ; MIPS32: G_BR %bb.2 + ; MIPS32: bb.1.end: + ; MIPS32: $v0 = COPY [[COPY]](s32) + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.block: + ; MIPS32: successors: %bb.1(0x80000000) + ; MIPS32: G_BR %bb.1 + bb.1.entry: + liveins: $a0, $a1 + + %0:_(s32) = COPY $a0 + G_BR %bb.3 + + bb.2.end: + $v0 = COPY %0(s32) + RetRA implicit $v0 + + bb.3.block: + G_BR %bb.2 + +... +--- +name: Conditional_branch +alignment: 2 +legalized: true +tracksRegLiveness: true +body: | + ; MIPS32-LABEL: name: Conditional_branch + ; MIPS32: bb.0 (%ir-block.0): + ; MIPS32: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; MIPS32: liveins: $a0, $a1, $a2 + ; MIPS32: [[COPY:%[0-9]+]]:gprb(s32) = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gprb(s32) = COPY $a1 + ; MIPS32: [[COPY2:%[0-9]+]]:gprb(s32) = COPY $a2 + ; MIPS32: [[C:%[0-9]+]]:gprb(s32) = G_CONSTANT i32 1 + ; MIPS32: [[COPY3:%[0-9]+]]:gprb(s32) = COPY [[COPY]](s32) + ; MIPS32: [[AND:%[0-9]+]]:gprb(s32) = G_AND [[COPY3]], [[C]] + ; MIPS32: G_BRCOND [[AND]](s32), %bb.1 + ; MIPS32: G_BR %bb.2 + ; MIPS32: bb.1.if.then: + ; MIPS32: $v0 = COPY [[COPY1]](s32) + ; MIPS32: RetRA implicit $v0 + ; MIPS32: bb.2.if.else: + ; MIPS32: $v0 = COPY [[COPY2]](s32) + ; MIPS32: RetRA implicit $v0 + bb.1 (%ir-block.0): + liveins: $a0, $a1, $a2 + + %3:_(s32) = COPY $a0 + %1:_(s32) = COPY $a1 + %2:_(s32) = COPY $a2 + %5:_(s32) = G_CONSTANT i32 1 + %6:_(s32) = COPY %3(s32) + %4:_(s32) = G_AND %6, %5 + G_BRCOND %4(s32), %bb.2 + G_BR %bb.3 + + bb.2.if.then: + $v0 = COPY %1(s32) + RetRA implicit $v0 + + bb.3.if.else: + $v0 = COPY %2(s32) + RetRA implicit $v0 + +... Index: test/CodeGen/Thumb2/peephole-addsub.mir =================================================================== --- /dev/null +++ test/CodeGen/Thumb2/peephole-addsub.mir @@ -0,0 +1,35 @@ +# RUN: llc -run-pass=peephole-opt -verify-machineinstrs -o - %s | FileCheck %s +--- | + target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" + target triple = "thumbv7-none-eabi" + + define i32 @test(i32 %a, i32 %b) { + unreachable + } + +... +--- +name: test +tracksRegLiveness: true +liveins: + - { reg: '$r0', virtual-reg: '%0' } + - { reg: '$r1', virtual-reg: '%1' } +body: | + bb.0 (%ir-block.0): + liveins: $r0, $r1 + + %1:rgpr = COPY $r1 + %0:rgpr = COPY $r0 + %2:rgpr = t2MOVi 1, 14, $noreg, $noreg + %3:gprnopc = t2ADDrr %0, %1, 14, $noreg, $noreg + %4:gprnopc = t2SUBri %3, 0, 14, $noreg, def dead $cpsr + t2CMPri killed %3, 0, 14, $noreg, implicit-def $cpsr + %5:rgpr = t2MOVCCi %2, 0, 7, $cpsr + $r0 = COPY %5 + tBX_RET 14, $noreg, implicit $r0 + +# CHECK-LABEL: name: test +# CHECK: %3:gprnopc = t2ADDrr %0, %1, 14, $noreg, $noreg +# CHECK-NEXT: %4:gprnopc = t2SUBri %3, 0, 14, $noreg, def $cpsr +# CHECK-NEXT: %5:rgpr = t2MOVCCi %2, 0, 7, $cpsr +... Index: test/CodeGen/X86/memcmp.ll =================================================================== --- test/CodeGen/X86/memcmp.ll +++ test/CodeGen/X86/memcmp.ll @@ -1344,3 +1344,70 @@ %m = tail call i32 @memcmp(i8* %X, i8* %Y, i64 9223372036854775807) nounwind ret i32 %m } + +define i1 @huge_length_eq(i8* %X, i8* %Y) nounwind { +; X86-LABEL: huge_length_eq: +; X86: # %bb.0: +; X86-NEXT: pushl $2147483647 # imm = 0x7FFFFFFF +; X86-NEXT: pushl $-1 +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: calll memcmp +; X86-NEXT: addl $16, %esp +; X86-NEXT: testl %eax, %eax +; X86-NEXT: sete %al +; X86-NEXT: retl +; +; X64-LABEL: huge_length_eq: +; X64: # %bb.0: +; X64-NEXT: pushq %rax +; X64-NEXT: movabsq $9223372036854775807, %rdx # imm = 0x7FFFFFFFFFFFFFFF +; X64-NEXT: callq memcmp +; X64-NEXT: testl %eax, %eax +; X64-NEXT: sete %al +; X64-NEXT: popq %rcx +; X64-NEXT: retq + + %m = tail call i32 @memcmp(i8* %X, i8* %Y, i64 9223372036854775807) nounwind + %c = icmp eq i32 %m, 0 + ret i1 %c +} + +; This checks non-constant sizes. +define i32 @nonconst_length(i8* %X, i8* %Y, i64 %size) nounwind { +; X86-LABEL: nonconst_length: +; X86: # %bb.0: +; X86-NEXT: jmp memcmp # TAILCALL +; +; X64-LABEL: nonconst_length: +; X64: # %bb.0: +; X64-NEXT: jmp memcmp # TAILCALL + %m = tail call i32 @memcmp(i8* %X, i8* %Y, i64 %size) nounwind + ret i32 %m +} + +define i1 @nonconst_length_eq(i8* %X, i8* %Y, i64 %size) nounwind { +; X86-LABEL: nonconst_length_eq: +; X86: # %bb.0: +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: calll memcmp +; X86-NEXT: addl $16, %esp +; X86-NEXT: testl %eax, %eax +; X86-NEXT: sete %al +; X86-NEXT: retl +; +; X64-LABEL: nonconst_length_eq: +; X64: # %bb.0: +; X64-NEXT: pushq %rax +; X64-NEXT: callq memcmp +; X64-NEXT: testl %eax, %eax +; X64-NEXT: sete %al +; X64-NEXT: popq %rcx +; X64-NEXT: retq + %m = tail call i32 @memcmp(i8* %X, i8* %Y, i64 %size) nounwind + %c = icmp eq i32 %m, 0 + ret i1 %c +} Index: test/Transforms/InferFunctionAttrs/annotate.ll =================================================================== --- test/Transforms/InferFunctionAttrs/annotate.ll +++ test/Transforms/InferFunctionAttrs/annotate.ll @@ -1,5 +1,5 @@ -; RUN: opt < %s -mtriple=x86_64-- -inferattrs -S | FileCheck %s -; RUN: opt < %s -mtriple=x86_64-- -passes=inferattrs -S | FileCheck %s +; RUN: opt < %s -mtriple=x86_64-- -inferattrs -S | FileCheck -check-prefix=CHECK-UNKNOWN %s +; RUN: opt < %s -mtriple=x86_64-- -passes=inferattrs -S | FileCheck -check-prefix=CHECK-UNKNOWN %s ; RUN: opt < %s -mtriple=x86_64-apple-macosx10.8.0 -inferattrs -S | FileCheck -check-prefix=CHECK -check-prefix=CHECK-DARWIN %s ; RUN: opt < %s -mtriple=x86_64-unknown-linux-gnu -inferattrs -S | FileCheck -check-prefix=CHECK -check-prefix=CHECK-LINUX %s ; RUN: opt < %s -mtriple=nvptx -inferattrs -S | FileCheck -check-prefix=CHECK-NVPTX %s @@ -241,7 +241,10 @@ ; CHECK: declare i64 @atoll(i8* nocapture) [[G1]] declare i64 @atoll(i8*) -; CHECK: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G1]] +; CHECK-DARWIN: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G1]] +; CHECK-LINUX: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G1]] +; CHECK-UNKNOWN-NOT: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G1]] +; CHECK-NVPTX-NOT: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G1]] declare i32 @bcmp(i8*, i8*, i64) ; CHECK: declare void @bcopy(i8* nocapture readonly, i8* nocapture, i64) [[G0]] Index: test/Transforms/InstCombine/memcmp-1.ll =================================================================== --- test/Transforms/InstCombine/memcmp-1.ll +++ test/Transforms/InstCombine/memcmp-1.ll @@ -1,6 +1,7 @@ ; Test that the memcmp library call simplifier works correctly. ; -; RUN: opt < %s -instcombine -S | FileCheck %s +; RUN: opt < %s -instcombine -S | FileCheck --check-prefix=CHECK --check-prefix=NOBCMP %s +; RUN: opt < %s -instcombine -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefix=CHECK --check-prefix=BCMP %s 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-n8:16:32:64" @@ -130,3 +131,21 @@ %cmp = icmp eq i32 %call, 0 ret i1 %cmp } + +; Check memcmp(mem1, mem2, size)==0 -> bcmp(mem1, mem2, size)==0 + +define i1 @test_simplify10(i8* %mem1, i8* %mem2, i32 %size) { +; NOBCMP-LABEL: @test_simplify10( +; NOBCMP-NEXT: [[CALL:%.*]] = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 %size) +; NOBCMP-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 +; NOBCMP-NEXT: ret i1 [[CMP]] +; +; BCMP-LABEL: @test_simplify10( +; BCMP-NEXT: [[CALL:%.*]] = call i32 @bcmp(i8* %mem1, i8* %mem2, i32 %size) +; BCMP-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 +; BCMP-NEXT: ret i1 [[CMP]] +; + %call = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 %size) + %cmp = icmp eq i32 %call, 0 + ret i1 %cmp +} Index: test/Transforms/InstCombine/strcmp-1.ll =================================================================== --- test/Transforms/InstCombine/strcmp-1.ll +++ test/Transforms/InstCombine/strcmp-1.ll @@ -1,5 +1,6 @@ ; Test that the strcmp library call simplifier works correctly. -; RUN: opt < %s -instcombine -S | FileCheck %s +; RUN: opt < %s -instcombine -S | FileCheck %s --check-prefix=NOBCMP +; RUN: opt < %s -instcombine -mtriple=unknown-unknown-linux-gnu -S | FileCheck %s --check-prefix=BCMP 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" @@ -80,3 +81,24 @@ %temp1 = call i32 @strcmp(i8* %str, i8* %str) ret i32 %temp1 } + +; strcmp(x, y) == 0 -> bcmp(x, y, ) +define i1 @test7(i1 %b) { +; BCMP-LABEL: @test7( +; BCMP: %bcmp = call i32 @bcmp(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i32 0, i32 0), i8* %str2, i32 5) +; BCMP: %res = icmp eq i32 %bcmp, 0 +; BCMP: ret i1 %res + +; NOBCMP-LABEL: @test7( +; NOBCMP: %memcmp = call i32 @memcmp(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i32 0, i32 0), i8* %str2, i32 5) +; NOBCMP: %res = icmp eq i32 %memcmp, 0 +; NOBCMP: ret i1 %res + + %str1 = getelementptr inbounds [6 x i8], [6 x i8]* @hello, i32 0, i32 0 + %temp1 = getelementptr inbounds [5 x i8], [5 x i8]* @hell, i32 0, i32 0 + %temp2 = getelementptr inbounds [5 x i8], [5 x i8]* @bell, i32 0, i32 0 + %str2 = select i1 %b, i8* %temp1, i8* %temp2 + %temp3 = call i32 @strcmp(i8* %str1, i8* %str2) + %res = icmp eq i32 %temp3, 0 + ret i1 %res +}