strlen in fast-isel

It seems that fast-isel for intel does not handle strlen. It's a general problem in fast-isel .

~/llvmw/build/Deb~/llvmw/build/Debug+Asserts/bin/clang -O0 -mllvm -fast-isel-verbose -mllvm -fast-isel strlen1.c
strlen1.c:12:3: warning: implicitly declaring library function 'printf' with
       type 'int (const char *, ...)'
   printf("%i\n", len);
   ^
strlen1.c:12:3: note: include the header <stdio.h> or explicitly provide a
       declaration for 'printf'
FastISel missed call: %call = call i64 @strlen(i8* %0) #3
1 warning generated.

#include <string.h>

char *hello = "hello";
int len;

void foo() {
   len = strlen(hello);
}

int main() {
   foo();
   printf("%i\n", len);
}

This issue occurs in a many library functions.
strlen, strcmp, ... many others.
I'm surprised nobody has noticed this because many basic blocks will fail to be compiled as fast-isel in this case.

It seems like just a bug in:
bool FastISel::selectInstruction(const Instruction *I) {

this function returns false but then it causes fast-isel to just do a miss on the function. there is no way to override the behavior.

Since this is happening on a call instructtion, it's going to drop back and print as a missed fast isel call.

It seems like this test in
bool FastISel::selectInstruction(const Instruction *I)

should just be deleted.

Thoughts?

     // As a special case, don't handle calls to builtin library functions that
     // may be translated directly to target instructions.
     if (F && !F->hasLocalLinkage() && F->hasName() &&
         LibInfo->getLibFunc(F->getName(), Func) &&
         LibInfo->hasOptimizedCodeGen(Func))
       return false;

This is called from void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn)

     if (FastIS->selectInstruction(Inst)) {
           --NumFastIselRemaining;
           ++NumFastIselSuccess;
           // If fast isel succeeded, skip over all the folded instructions, and
           // then see if there is a load right before the selected instructions.
           // Try to fold the load if so.
           const Instruction *BeforeInst = Inst;
           while (BeforeInst != Begin) {
             BeforeInst = std::prev(BasicBlock::const_iterator(BeforeInst));
             if (!isFoldedOrDeadInstruction(BeforeInst, FuncInfo))
               break;
           }
           if (BeforeInst != Inst && isa<LoadInst>(BeforeInst) &&
               BeforeInst->hasOneUse() &&
               FastIS->tryToFoldLoad(cast<LoadInst>(BeforeInst), Inst)) {
             // If we succeeded, don't re-select the load.
             BI = std::next(BasicBlock::const_iterator(BeforeInst));
             --NumFastIselRemaining;
             ++NumFastIselSuccess;
           }
           continue;
         }

#ifndef NDEBUG
         if (EnableFastISelVerbose2)
           collectFailStats(Inst);
#endif

         // Then handle certain instructions as single-LLVM-Instruction blocks.
         if (isa<CallInst>(Inst)) {

           if (EnableFastISelVerbose || EnableFastISelAbort) {
             dbgs() << "FastISel missed call: ";
             Inst->dump();

Any basic blocks with these functions are going to get passed over by fast-isel:

   bool hasOptimizedCodeGen(LibFunc::Func F) const {
     if (Impl->getState(F) == TargetLibraryInfoImpl::Unavailable)
       return false;
     switch (F) {
     default: break;
     case LibFunc::copysign: case LibFunc::copysignf: case LibFunc::copysignl:
     case LibFunc::fabs: case LibFunc::fabsf: case LibFunc::fabsl:
     case LibFunc::sin: case LibFunc::sinf: case LibFunc::sinl:
     case LibFunc::cos: case LibFunc::cosf: case LibFunc::cosl:
     case LibFunc::sqrt: case LibFunc::sqrtf: case LibFunc::sqrtl:
     case LibFunc::sqrt_finite: case LibFunc::sqrtf_finite:
                                                   case LibFunc::sqrtl_finite:
     case LibFunc::fmax: case LibFunc::fmaxf: case LibFunc::fmaxl:
     case LibFunc::fmin: case LibFunc::fminf: case LibFunc::fminl:
     case LibFunc::floor: case LibFunc::floorf: case LibFunc::floorl:
     case LibFunc::nearbyint: case LibFunc::nearbyintf: case LibFunc::nearbyintl:
     case LibFunc::ceil: case LibFunc::ceilf: case LibFunc::ceill:
     case LibFunc::rint: case LibFunc::rintf: case LibFunc::rintl:
     case LibFunc::round: case LibFunc::roundf: case LibFunc::roundl:
     case LibFunc::trunc: case LibFunc::truncf: case LibFunc::truncl:
     case LibFunc::log2: case LibFunc::log2f: case LibFunc::log2l:
     case LibFunc::exp2: case LibFunc::exp2f: case LibFunc::exp2l:
     case LibFunc::memcmp: case LibFunc::strcmp: case LibFunc::strcpy:
     case LibFunc::stpcpy: case LibFunc::strlen: case LibFunc::strnlen:
     case LibFunc::memchr:
       return true;
     }
     return false;
   }

Seems like it might possible to override TargetLIbraryInfo during the creation of the fast-isel object.

I will look into this.

namespace llvm {
FastISel *Mips::createFastISel(FunctionLoweringInfo &funcInfo,
                                const TargetLibraryInfo *libInfo) {
   return new MipsFastISel(funcInfo, libInfo);
}

-fno-builtin

will also make this problem go away.