void ResolutionCandidate::resolveTypeConstructor(CallInfo& info) {
  SET_LINENO(fn);

  // Ignore tuple constructors; they were generated
  // with their type constructors.
  if (fn->hasFlag(FLAG_PARTIAL_TUPLE) == false) {
    CallExpr* typeConstructorCall = new CallExpr(astr("_type", fn->name));

    for_formals(formal, fn) {
      if (formal->hasFlag(FLAG_IS_MEME) == false) {
        if (fn->_this->type->symbol->hasFlag(FLAG_TUPLE)) {
          if (formal->instantiatedFrom != NULL) {
            typeConstructorCall->insertAtTail(formal->type->symbol);

          } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) {
            typeConstructorCall->insertAtTail(paramMap.get(formal));
          }

        } else {
          if (strcmp(formal->name, "outer") == 0 ||
              formal->type                  == dtMethodToken) {
            typeConstructorCall->insertAtTail(formal);

          } else if (formal->instantiatedFrom != NULL) {
            SymExpr*   se = new SymExpr(formal->type->symbol);
            NamedExpr* ne = new NamedExpr(formal->name, se);

            typeConstructorCall->insertAtTail(ne);

          } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) {
            SymExpr*   se = new SymExpr(paramMap.get(formal));
            NamedExpr* ne = new NamedExpr(formal->name, se);

            typeConstructorCall->insertAtTail(ne);
          }
        }
      }
    }

    info.call->insertBefore(typeConstructorCall);

    // If instead we call resolveCallAndCallee(typeConstructorCall)
    // then the line number reported in an error would change
    // e.g.: domains/deitz/test_generic_class_of_sparse_domain
    // or:   classes/diten/multipledestructor
    resolveCall(typeConstructorCall);

    INT_ASSERT(typeConstructorCall->isResolved());

    resolveFunction(typeConstructorCall->resolvedFunction());

    fn->_this->type = typeConstructorCall->resolvedFunction()->retType;

    typeConstructorCall->remove();
  }
Beispiel #2
0
void WTemplate::renderTemplateText(std::ostream& result, const WString& templateText)
{
  std::string text;

  WApplication *app = WApplication::instance();

  if (app && (encodeInternalPaths_ || app->session()->hasSessionIdInUrl())) {
    WFlags<RefEncoderOption> options;
    if (encodeInternalPaths_)
      options |= EncodeInternalPaths;
    if (app->session()->hasSessionIdInUrl())
      options |= EncodeRedirectTrampoline;
    WString t = templateText;
    EncodeRefs(t, options);
    text = t.toUTF8();
  } else
    text = templateText.toUTF8();

  std::size_t lastPos = 0;
  std::vector<WString> args;
  std::vector<std::string> conditions;
  int suppressing = 0;

  for (std::size_t pos = text.find('$'); pos != std::string::npos;
       pos = text.find('$', pos)) {

    if (!suppressing)
      result << text.substr(lastPos, pos - lastPos);

    lastPos = pos;

    if (pos + 1 < text.length()) {
      if (text[pos + 1] == '$') { // $$ -> $
	if (!suppressing)
	  result << '$';

	lastPos += 2;
      } else if (text[pos + 1] == '{') {
	std::size_t startName = pos + 2;
	std::size_t endName = text.find_first_of(" \r\n\t}", startName);

	args.clear();
	std::size_t endVar = parseArgs(text, endName, args);

	if (endVar == std::string::npos) {
	  LOG_ERROR("variable syntax error near \"" << text.substr(pos)
		    << "\"");
	  return;
	}

	std::string name = text.substr(startName, endName - startName);
	std::size_t nl = name.length();

	if (nl > 2 && name[0] == '<' && name[nl - 1] == '>') {
	  if (name[1] != '/') {
	    std::string cond = name.substr(1, nl - 2);
	    conditions.push_back(cond);
	    if (suppressing || !conditionValue(cond))
	      ++suppressing;
	  } else {
	    std::string cond = name.substr(2, nl - 3);
	    if (conditions.back() != cond) {
	      LOG_ERROR("mismatching condition block end: " << cond);
	      return;
	    }
	    conditions.pop_back();

	    if (suppressing)
	      --suppressing;
	  }
	} else {
	  if (!suppressing) {
	    std::size_t colonPos = name.find(':');

	    bool handled = false;
	    if (colonPos != std::string::npos) {
	      std::string fname = name.substr(0, colonPos);
	      std::string arg0 = name.substr(colonPos + 1);
	      args.insert(args.begin(), WString::fromUTF8(arg0));
	      if (resolveFunction(fname, args, result))
		handled = true;
	      else
		args.erase(args.begin());
	    }

	    if (!handled)
	      resolveString(name, args, result);
	  }
	}

	lastPos = endVar + 1;
      } else {
	if (!suppressing)
	  result << '$'; // $. -> $.
	lastPos += 1;
      }
    } else {
      if (!suppressing)
	result << '$'; // $ at end of template -> $
      lastPos += 1;
    }

    pos = lastPos;
  }

  result << text.substr(lastPos);
}
Beispiel #3
0
void printTrace()
{
	SymInitialize(GetCurrentProcess(), NULL, TRUE);

	UINT32 maxframes = 62;
	UINT_PTR myFrames[62];

	ZeroMemory(myFrames, sizeof(UINT_PTR) * maxframes);
	ULONG BackTraceHash;
	maxframes = CaptureStackBackTrace(0, maxframes, reinterpret_cast<PVOID*>(myFrames), &BackTraceHash);

	const UINT_PTR* pFrame = myFrames;
	const size_t frameSize = maxframes;

	UINT32  startIndex = 0;

	int unresolvedFunctionsCount = 0;
	IMAGEHLP_LINE  sourceInfo = { 0 };
	sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE);

	// Use static here to increase performance, and avoid heap allocs.
	// It's thread safe because of g_heapMapLock lock.
	static char stack_line[1024] = "";
	bool isPrevFrameInternal = false;
	DWORD NumChars = 0;

	const size_t max_line_length = 512;
	const int resolvedCapacity = 62 * max_line_length;
	const size_t allocedBytes = resolvedCapacity * sizeof(char);
	char resolved[resolvedCapacity];
	if (resolved) {
		ZeroMemory(resolved, allocedBytes);
	}
	HANDLE hProcess = GetCurrentProcess();
	int resolvedLength = 0;
	// Iterate through each frame in the call stack.
	for (UINT32 frame = 0; frame < frameSize; frame++)
	{
		if (pFrame[frame] == 0)
			break;
		// Try to get the source file and line number associated with
		// this program counter address.
		SIZE_T programCounter = pFrame[frame];

		DWORD64 displacement64;
		BYTE symbolBuffer[sizeof(SYMBOL_INFO) + 256 * sizeof(char)];
		LPCSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer);

		// It turns out that calls to SymGetLineFromAddrW64 may free the very memory we are scrutinizing here
		// in this method. If this is the case, m_Resolved will be null after SymGetLineFromAddrW64 returns.
		// When that happens there is nothing we can do except crash.
		DWORD            displacement = 0;

		BOOL foundline = SymGetLineFromAddr(hProcess, programCounter, &displacement, &sourceInfo);

		bool isFrameInternal = false;

		// show one allocation function for context
		if (NumChars > 0 && !isFrameInternal && isPrevFrameInternal) {
			resolvedLength += NumChars;
			if (resolved) {
				strncat_s(resolved, resolvedCapacity, stack_line, NumChars);
			}
		}
		isPrevFrameInternal = isFrameInternal;

		if (!foundline)
			displacement = (DWORD)displacement64;
		NumChars = resolveFunction(programCounter, foundline ? &sourceInfo : NULL,
			displacement, functionName, stack_line, _countof(stack_line));

		if (NumChars > 0 && !isFrameInternal) {
			resolvedLength += NumChars;
			if (resolved) {
				strncat_s(resolved, resolvedCapacity, stack_line, NumChars);
			}
		}
	} // end for loop
	printLog(resolved);

	SymCleanup(GetCurrentProcess());
	return;
}
Beispiel #4
0
bool WTemplate::renderTemplateText(std::ostream& result, const WString& templateText)
{
  errorText_ = "";

  std::string text;
  if (encodeTemplateText_)
    text = encode(templateText.toUTF8());
  else
    text = templateText.toUTF8();

  std::size_t lastPos = 0;
  std::vector<WString> args;
  std::vector<std::string> conditions;
  int suppressing = 0;

  for (std::size_t pos = text.find('$'); pos != std::string::npos;
       pos = text.find('$', pos)) {

    if (!suppressing)
      result << text.substr(lastPos, pos - lastPos);

    lastPos = pos;

    if (pos + 1 < text.length()) {
      if (text[pos + 1] == '$') { // $$ -> $
	if (!suppressing)
	  result << '$';

	lastPos += 2;
      } else if (text[pos + 1] == '{') {
	std::size_t startName = pos + 2;
	std::size_t endName = text.find_first_of(" \r\n\t}", startName);

        args.clear();
        std::size_t endVar = parseArgs(text, endName, args);

        if (endVar == std::string::npos) {
          std::stringstream errorStream;
          errorStream << "variable syntax error near \"" << text.substr(pos)
                      << "\"";
          errorText_ = errorStream.str();
          LOG_ERROR(errorText_);
          return false;
        }

        std::string name = text.substr(startName, endName - startName);
	std::size_t nl = name.length();

	if (nl > 2 && name[0] == '<' && name[nl - 1] == '>') {
	  if (name[1] != '/') {
	    std::string cond = name.substr(1, nl - 2);
	    conditions.push_back(cond);
	    if (suppressing || !conditionValue(cond))
	      ++suppressing;
	  } else {
	    std::string cond = name.substr(2, nl - 3);
	    if (conditions.empty() || conditions.back() != cond) {
              std::stringstream errorStream;
              errorStream << "mismatching condition block end: " << cond;
              errorText_ = errorStream.str();
              LOG_ERROR(errorText_);
              return false;
	    }
	    conditions.pop_back();

	    if (suppressing)
	      --suppressing;
	  }
	} else {
	  if (!suppressing) {
	    std::size_t colonPos = name.find(':');

	    bool handled = false;
	    if (colonPos != std::string::npos) {
	      std::string fname = name.substr(0, colonPos);
	      std::string arg0 = name.substr(colonPos + 1);
	      args.insert(args.begin(), WString::fromUTF8(arg0));
	      if (resolveFunction(fname, args, result))
		handled = true;
	      else
		args.erase(args.begin());
	    }

	    if (!handled)
	      resolveString(name, args, result);
	  }
	}

	lastPos = endVar + 1;
      } else {
	if (!suppressing)
	  result << '$'; // $. -> $.
	lastPos += 1;
      }
    } else {
      if (!suppressing)
	result << '$'; // $ at end of template -> $
      lastPos += 1;
    }

    pos = lastPos;
  }

  result << text.substr(lastPos);
  return true;
}
  /*
   * Reduce this module with respect to the given interface.
   * - The interface suggests some of the uses of the functions,
   *   so here we can generate special versions of those functions.
   * Generate a ComponentInterfaceTransform for clients to rewrite their
   * code to use the new API
   */
  bool
  SpecializeComponent(Module& M, ComponentInterfaceTransform& T,
      SpecializationPolicy &policy, std::list<Function*>& to_add)
  {
    int rewrite_count = 0;
    const ComponentInterface& I = T.getInterface();
    // TODO: What needs to be done?
    // - Should try to handle strings & arrays
    for (ComponentInterface::FunctionIterator ff = I.begin(), fe = I.end(); ff
        != fe; ++ff) {
      StringRef name = ff->first();
      Function* func = resolveFunction(M, name);
      if (func == NULL || func->isDeclaration()) {
        // We don't specialize declarations because we don't own them
        continue;
      }

      for (ComponentInterface::CallIterator cc = I.call_begin(name), ce =
          I.call_end(name); cc != ce; ++cc) {
        const CallInfo* const call = *cc;

        const unsigned arg_count = call->args.size();

        if (func->isVarArg()) {
          // TODO: I don't know how to specialize variable argument functions yet
          continue;
        }
        if (arg_count != func->getArgumentList().size()) {
          // Not referring to this function?
          // NOTE: I can't assert this equality because of the way that approximations occur
          continue;
        }

        SmallBitVector slice(arg_count);
        bool shouldSpecialize = policy.specializeOn(func, call->args.begin(),
            call->args.end(), slice);

        if (!shouldSpecialize)
          continue;

        std::vector<Value*> args;
        std::vector<unsigned> argPerm;
        args.reserve(arg_count);
        argPerm.reserve(slice.count());
        for (unsigned i = 0; i < arg_count; i++) {
          if (slice.test(i)) {
	      Type * paramType = func->getFunctionType()->getParamType(i);
	      Value *concreteArg = call->args[i].concretize(M, paramType);
	      args.push_back(concreteArg);
	      assert(concreteArg->getType() == paramType
		     && "Specializing function with concrete argument of wrong type!");
          } else {
            args.push_back(NULL);
            argPerm.push_back(i);
          }
        }

        Function* nfunc = specializeFunction(func, args);
        nfunc->setLinkage(GlobalValue::ExternalLinkage);

        FunctionHandle rewriteTo = nfunc->getName();

        T.rewrite(name, call, rewriteTo, argPerm);

        to_add.push_back(nfunc);
        rewrite_count++;
      }
    }
    if (rewrite_count > 0) {
      errs() << rewrite_count << " pending rewrites\n";
    }
    return rewrite_count > 0;
  }
Beispiel #6
0
// Resolve - Creates a nicely formatted rendition of the CallStack, including
//   symbolic information (function names and line numbers) if available. and
//   saves it for later retrieval. This is almost identical to Callstack::dump above.
//
//   Note: The symbol handler must be initialized prior to calling this
//     function.
//
//  - showInternalFrames (IN): If true, then all frames in the CallStack will be
//      dumped. Otherwise, frames internal to the heap will not be dumped.
//
//  Return Value:
//
//    None.
//
int CallStack::resolve(BOOL showInternalFrames)
{
    if (m_resolved)
    {
        // already resolved, no need to do it again
        // resolving twice may report an incorrect module for the stack frames
        // if the memory was leaked in a dynamic library that was already unloaded.
        return 0;
    }

    if (m_status & CALLSTACK_STATUS_STARTUPCRT) {
        // there is no need to resolve a leak that will not be reported
        return 0;
    }

    if (m_status & CALLSTACK_STATUS_INCOMPLETE) {
        // This call stack appears to be incomplete. Using StackWalk64 may be
        // more reliable.
        Report(L"    HINT: The following call stack may be incomplete. Setting \"StackWalkMethod\"\n"
            L"      in the vld.ini file to \"safe\" instead of \"fast\" may result in a more\n"
            L"      complete stack trace.\n");
    }

    int unresolvedFunctionsCount = 0;
    IMAGEHLP_LINE64  sourceInfo = { 0 };
    sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

    bool skipStartupLeaks = !!(g_vld.GetOptions() & VLD_OPT_SKIP_CRTSTARTUP_LEAKS);

    // Use static here to increase performance, and avoid heap allocs.
    // It's thread safe because of g_heapMapLock lock.
    static WCHAR stack_line[MAXREPORTLENGTH + 1] = L"";
    bool isPrevFrameInternal = false;
    bool isDynamicInitializer = false;
    DWORD NumChars = 0;
    CriticalSectionLocker<DbgHelp> locker(g_DbgHelp);

    const size_t max_line_length = MAXREPORTLENGTH + 1;
    m_resolvedCapacity = m_size * max_line_length;
    const size_t allocedBytes = m_resolvedCapacity * sizeof(WCHAR);
    m_resolved = new WCHAR[m_resolvedCapacity];
    if (m_resolved) {
        ZeroMemory(m_resolved, allocedBytes);
    }

    // Iterate through each frame in the call stack.
    for (UINT32 frame = 0; frame < m_size; frame++)
    {
        // Try to get the source file and line number associated with
        // this program counter address.
        SIZE_T programCounter = (*this)[frame];
        DWORD            displacement = 0;

        // It turns out that calls to SymGetLineFromAddrW64 may free the very memory we are scrutinizing here
        // in this method. If this is the case, m_Resolved will be null after SymGetLineFromAddrW64 returns.
        // When that happens there is nothing we can do except crash.
        DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId());
        BOOL foundline = g_DbgHelp.SymGetLineFromAddrW64(g_currentProcess, programCounter, &displacement, &sourceInfo, locker);

        if (skipStartupLeaks && foundline && !isDynamicInitializer &&
            !(m_status & CALLSTACK_STATUS_NOTSTARTUPCRT) && isCrtStartupModule(sourceInfo.FileName)) {
            m_status |= CALLSTACK_STATUS_STARTUPCRT;
            delete[] m_resolved;
            m_resolved = NULL;
            m_resolvedCapacity = 0;
            m_resolvedLength = 0;
            return 0;
        }

        bool isFrameInternal = false;
        if (foundline && !showInternalFrames) {
            if (isInternalModule(sourceInfo.FileName)) {
                // Don't show frames in files internal to the heap.
                isFrameInternal = true;
            }
        }

        // show one allocation function for context
        if (NumChars > 0 && !isFrameInternal && isPrevFrameInternal) {
            m_resolvedLength += NumChars;
            if (m_resolved) {
                wcsncat_s(m_resolved, m_resolvedCapacity, stack_line, NumChars);
            }
        }
        isPrevFrameInternal = isFrameInternal;

        DWORD64 displacement64;
        BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE];
        LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker);

        if (skipStartupLeaks && foundline && beginWith(functionName, wcslen(functionName), L"`dynamic initializer for '")) {
            isDynamicInitializer = true;
        }

        if (!foundline)
            displacement = (DWORD)displacement64;
        NumChars = resolveFunction( programCounter, foundline ? &sourceInfo : NULL,
            displacement, functionName, stack_line, _countof( stack_line ));

        if (NumChars > 0 && !isFrameInternal) {
            m_resolvedLength += NumChars;
            if (m_resolved) {
                wcsncat_s(m_resolved, m_resolvedCapacity, stack_line, NumChars);
            }
        }
    } // end for loop

    m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT;
    return unresolvedFunctionsCount;
}
Beispiel #7
0
// dump - Dumps a nicely formatted rendition of the CallStack, including
//   symbolic information (function names and line numbers) if available.
//
//   Note: The symbol handler must be initialized prior to calling this
//     function.
//
//  - showinternalframes (IN): If true, then all frames in the CallStack will be
//      dumped. Otherwise, frames internal to the heap will not be dumped.
//
//  Return Value:
//
//    None.
//
void CallStack::dump(BOOL showInternalFrames, UINT start_frame) const
{
    // The stack was dumped already
    if (m_resolved)
    {
        dumpResolved();
        return;
    }

    if (m_status & CALLSTACK_STATUS_INCOMPLETE) {
        // This call stack appears to be incomplete. Using StackWalk64 may be
        // more reliable.
        Report(L"    HINT: The following call stack may be incomplete. Setting \"StackWalkMethod\"\n"
            L"      in the vld.ini file to \"safe\" instead of \"fast\" may result in a more\n"
            L"      complete stack trace.\n");
    }

    IMAGEHLP_LINE64  sourceInfo = { 0 };
    sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

    // Use static here to increase performance, and avoid heap allocs.
    // It's thread safe because of g_heapMapLock lock.
    static WCHAR stack_line[MAXREPORTLENGTH + 1] = L"";
    bool isPrevFrameInternal = false;
    CriticalSectionLocker<DbgHelp> locker(g_DbgHelp);

    // Iterate through each frame in the call stack.
    for (UINT32 frame = start_frame; frame < m_size; frame++)
    {
        // Try to get the source file and line number associated with
        // this program counter address.
        SIZE_T programCounter = (*this)[frame];
        BOOL             foundline = FALSE;
        DWORD            displacement = 0;
        DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId());
        foundline = g_DbgHelp.SymGetLineFromAddrW64(g_currentProcess, programCounter, &displacement, &sourceInfo, locker);

        bool isFrameInternal = false;
        if (foundline && !showInternalFrames) {
            if (isInternalModule(sourceInfo.FileName))
            {
                // Don't show frames in files internal to the heap.
                isFrameInternal = true;
            }
        }

        // show one allocation function for context
        if (!isFrameInternal && isPrevFrameInternal)
            Print(stack_line);
        isPrevFrameInternal = isFrameInternal;

        DWORD64 displacement64;
        BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE];
        LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker);

        if (!foundline)
            displacement = (DWORD)displacement64;
        DWORD NumChars = resolveFunction(programCounter, foundline ? &sourceInfo : NULL,
            displacement, functionName, stack_line, _countof(stack_line));
        UNREFERENCED_PARAMETER(NumChars);

        if (!isFrameInternal)
            Print(stack_line);
    }
}
Beispiel #8
0
  /*
   * Reduce this module with respect to the given interface.
   * - The interface suggests some of the uses of the functions,
   *   so here we can generate special versions of those functions.
   * Generate a ComponentInterfaceTransform for clients to rewrite their
   * code to use the new API
   */
  bool
  SpecializeComponent(Module& M, ComponentInterfaceTransform& T,
      SpecializationPolicy &policy, std::list<Function*>& to_add)
  {

    errs() << "SpecializeComponent()\n";
    

    int rewrite_count = 0;
    const ComponentInterface& I = T.getInterface();
    // TODO: What needs to be done?
    // - Should try to handle strings & arrays
    // Iterate through all functions in the interface of T 
    for (ComponentInterface::FunctionIterator ff = I.begin(), fe = I.end(); ff
        != fe; ++ff) {
      StringRef name = ff->first();
      Function* func = resolveFunction(M, name);

      if (func == NULL || func->isDeclaration()) {
        // We don't specialize declarations because we don't own them
        continue;
      }
      
      // Now iterate through all calls to func in the interface of T
      for (ComponentInterface::CallIterator cc = I.call_begin(name), ce =
          I.call_end(name); cc != ce; ++cc) {
        const CallInfo* const call = *cc;

        const unsigned arg_count = call->args.size();

        if (func->isVarArg()) {
          // TODO: I don't know how to specialize variable argument functions yet
          continue;
        }

        if (arg_count != func->getArgumentList().size()) {
          // Not referring to this function?
          // NOTE: I can't assert this equality because of the way that approximations occur
          continue;
        }

	/*
	  should we specialize? if yes then each bit in slice will indicate whether the argument is 
	  a specializable constant
	 */
        SmallBitVector slice(arg_count);
        bool shouldSpecialize = policy.specializeOn(func, call->args.begin(), call->args.end(), slice);

        if (!shouldSpecialize)
          continue;

        std::vector<Value*> args;
        std::vector<unsigned> argPerm;
        args.reserve(arg_count);
        argPerm.reserve(slice.count());
        for (unsigned i = 0; i < arg_count; i++) {
          if (slice.test(i)) {
	      Type * paramType = func->getFunctionType()->getParamType(i);
	      Value *concreteArg = call->args[i].concretize(M, paramType);
	      args.push_back(concreteArg);
	      assert(concreteArg->getType() == paramType
		     && "Specializing function with concrete argument of wrong type!");
          } else {
            args.push_back(NULL);
            argPerm.push_back(i);
          }
        }
	/*
	  args is a list of pointers to values
	   --  if the pointer is NULL then that argument is not specialized
	   -- if the pointer is not NULL then the argument will be/has been specialized to that value
	   
	  argsPerm is a list on integers; the indices of the non-special arguments

	  args[i] = NULL iff i is in argsPerm for i < arg_count.

	*/

        Function* nfunc = specializeFunction(func, args);
        nfunc->setLinkage(GlobalValue::ExternalLinkage);

        FunctionHandle rewriteTo = nfunc->getName();

        T.rewrite(name, call, rewriteTo, argPerm);

        to_add.push_back(nfunc);
	
	
	errs() << "Specialized  " << name << " to " << rewriteTo << "\n";

#if 0
	for (unsigned i = 0; i < arg_count; i++) {
	  errs() << "i = " << i << ": slice[i] = " << slice[i] 
		 << " args[i] = " << args.at(i) << "\n";
	}
	errs() << " argPerm = [";
	for (unsigned i = 0; i < argPerm.size(); i++) {
	  errs() << argPerm.at(i) << " ";
	}
	errs() << "]\n";
#endif	

       rewrite_count++;
      }
    }
    if (rewrite_count > 0) {
      errs() << rewrite_count << " pending rewrites\n";
    }
    return rewrite_count > 0;
  }