/*
 * Copyright 2020-2022 Hewlett Packard Enterprise Development LP
 * Copyright 2004-2019 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "codegen.h"

#include "astutil.h"
#include "baseAST.h"
#include "clangBuiltinsWrappedSet.h"
#include "clangUtil.h"
#include "config.h"
#include "driver.h"
#include "expr.h"
#include "files.h"
#include "fixupExports.h"
#include "insertLineNumbers.h"
#include "library.h"
#include "llvmDebug.h"
#include "llvmUtil.h"
#include "LayeredValueTable.h"
#include "mli.h"
#include "misc.h"
#include "mysystem.h"
#include "passes.h"
#include "stlUtil.h"
#include "stmt.h"
#include "stringutil.h"
#include "symbol.h"
#include "typeSpecifier.h"
#include "view.h"
#include "virtualDispatch.h"
#include "chpl/util/filesystem.h"

#include "global-ast-vecs.h"

#ifdef HAVE_LLVM
// Include relevant LLVM headers
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#endif

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#include <inttypes.h>

#include <algorithm>
#include <cctype>

#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>

#include <sys/types.h>
#include <sys/wait.h>

// function prototypes
static bool compareSymbol(const void* v1, const void* v2);

// Global so that we don't have to pass around
// to all of the codegen() routines
GenInfo* gGenInfo   =  0;
int      gMaxVMT    = -1;
int      gStmtCount =  0;
bool     gCodegenGPU = false;

std::map<std::string, int> commIDMap;


// ensure these two produce consistent output
std::string zlineToString(BaseAST* ast) {
  return "/* ZLINE: " + numToString(ast->linenum())
         + " " + ast->fname() + " */\n";
}
void zlineToFileIfNeeded(BaseAST* ast, FILE* outfile) {
  if (printCppLineno)
    fprintf(outfile, "%s", zlineToString(ast).c_str());
}

static char idCommentBuffer[32];

const char* idCommentTemp(BaseAST* ast) {
  sprintf(idCommentBuffer, "/* %7d */ ", ast->id);
  return idCommentBuffer;
}


const char* legalizeName(const char* name) {
  std::string ret = "";

  for (const char* ch = name; *ch != '\0'; ch++) {
    switch (*ch) {
      case '=':
        if (*(ch+1) == '=') { // matched ==
          ret += "_EQUALS_";
          ch++;
        } else {
          ret += "_ASSIGN_";
        }
        break;

      case '>': ret += "_GREATER_";     break;
      case '<': ret += "_LESS_";        break;
      case '*': ret += "_ASTERISK_";    break;
      case '/': ret += "_SLASH_";       break;
      case '%': ret += "_PERCENT_";     break;
      case '+': ret += "_PLUS_";        break;
      case '-': ret += "_HYPHEN_";      break;
      case '^': ret += "_CARET_";       break;
      case '&': ret += "_AMPERSAND_";   break;
      case '|': ret += "_BAR_";         break;
      case '!': ret += "_EXCLAMATION_"; break;
      case '#': ret += "_POUND_";       break;
      case '?': ret += "_QUESTION_";    break;
      case '$': ret += "_DOLLAR_";      break;
      case '~': ret += "_TILDE_";       break;
      case ':': ret += "_COLON_";       break;
      case '.': ret += "_DOT_";         break;
      case ' ': ret +=  "_SPACE_";      break;
      default:
      {
        char c = *ch;
        ret += c;
        break;
      }
    }
  }

  return astr(ret.c_str());
}

static void legalizeSymbolName(Symbol* sym) {
  if (!fLibraryCompile && !sym->isRenameable())
    return;

  const char* newName = legalizeName(sym->cname);

  // Error when an exported function has an invalid C name, e.g. the module
  // initializer for a module named 'hyphenated-name.chpl'.
  if (fLibraryCompile) {
    if (FnSymbol* fn = toFnSymbol(sym)) {
      if (fn->hasFlag(FLAG_EXPORT) && isUserRoutine(fn) &&
          strcmp(sym->cname, newName)) {
        const char* fmt = fn->hasFlag(FLAG_MODULE_INIT)
            ? "Cannot export module initializer with name '%s'"
            : "Cannot export function with name '%s'";

        USR_FATAL_CONT(fmt, sym->cname);

        // If it's a module initializer, hint at changing the module name.
        if (fn->hasFlag(FLAG_MODULE_INIT)) {
          ModuleSymbol* mod = fn->getModule();
          USR_PRINT("Consider changing the name of module '%s' to be a "
                    "valid C identifier",
                    mod->name);
        }
      }
    }
  }

  if (!sym->isRenameable())
    return;

  // Everything is fine, set the new legalized name.
  sym->cname = newName;

  // Add chpl_ to operator names.
  if ((sym->cname[0] == '_' &&
      (sym->cname[1] == '_' || (sym->cname[1] >= 'A' && sym->cname[1] <= 'Z'))))
  {
    sym->cname = astr("chpl__", sym->cname);
  }

  // Append number of array dimensions to polly_array_index
  // It helps Polly Optimizer to select the correct function
  if (strcmp("polly_array_index",sym->name) == 0){
    int numDims = (toFnSymbol(sym)->numFormals() - 1) / 2;
    sym->cname = astr("polly_array_index_",istr(numDims));
  }
}

static void
genGlobalDefClassId(const char* cname, int id, bool isHeader) {
  GenInfo* info = gGenInfo;
  const char* id_type_name = "chpl__class_id";
  std::string name("chpl__cid_");
  name += cname;

  if( info->cfile ) {
    if(isHeader)
      fprintf(info->cfile, "extern const %s %s;\n",
                      id_type_name, name.c_str());
    else
      fprintf(info->cfile, "const %s %s = %d;\n",
                      id_type_name, name.c_str(), id);
  } else {
#ifdef HAVE_LLVM
    if (!isHeader)
      return;
    GenRet id_type_g = CLASS_ID_TYPE->codegen();
    llvm::Type *id_type = id_type_g.type;
    llvm::GlobalVariable * gv = llvm::cast<llvm::GlobalVariable>(
        info->module->getOrInsertGlobal(name, id_type));
    gv->setInitializer(info->irBuilder->getInt32(id));
    gv->setConstant(true);
    info->lvt->addGlobalValue(name, gv, GEN_PTR, ! is_signed(CLASS_ID_TYPE));
#endif
  }
}
static void
genGlobalString(const char *cname, const char *value) {
  GenInfo* info = gGenInfo;
  if( info->cfile ) {
    fprintf(info->cfile, "const char* %s = \"%s\";\n", cname, value);
  } else {
#ifdef HAVE_LLVM
    if(gCodegenGPU == false) {
      llvm::GlobalVariable *globalString = llvm::cast<llvm::GlobalVariable>(
          info->module->getOrInsertGlobal(
            cname, llvm::IntegerType::getInt8PtrTy(info->module->getContext())));
      globalString->setInitializer(llvm::cast<llvm::GlobalVariable>(
            new_CStringSymbol(value)->codegen().val)->getInitializer());
      globalString->setConstant(true);
      info->lvt->addGlobalValue(cname, globalString, GEN_PTR, true);
    }
#endif
  }
}

#ifdef HAVE_LLVM
static void genGlobalRawString(const char *cname, std::string &value, size_t len) {
  GenInfo* info = gGenInfo;
  if( info->cfile ) {
    // TODO: Currently we don't have this codepath. Maybe one day we will. If so, we
    // will add escapes.
    INT_FATAL("Do not expect to see this codepath");
  } else {
    if(gCodegenGPU == false) {
      llvm::GlobalVariable *globalString = llvm::cast<llvm::GlobalVariable>(
              info->module->getOrInsertGlobal(
                      cname, llvm::IntegerType::getInt8PtrTy(info->module->getContext())));
      auto globalStringIr = info->irBuilder->CreateGlobalString(value);
      auto correctlyTypedValue = info->irBuilder->CreateConstInBoundsGEP2_32(
        NULL, globalStringIr, 0, 0);
      globalString->setInitializer(llvm::cast<llvm::Constant>(correctlyTypedValue));
      globalString->setConstant(true);
      info->lvt->addGlobalValue(cname, globalString, GEN_PTR, true);
    }
  }
}
#endif

static void
genGlobalInt(const char* cname, int value, bool isHeader) {
  GenInfo* info = gGenInfo;
  if( info->cfile ) {
    if(isHeader)
      fprintf(info->cfile, "extern const int %s;\n", cname);
    else
    fprintf(info->cfile, "const int %s = %d;\n", cname, value);
  } else {
#ifdef HAVE_LLVM
    llvm::GlobalVariable *globalInt = llvm::cast<llvm::GlobalVariable>(
        info->module->getOrInsertGlobal(
          cname, llvm::IntegerType::getInt32Ty(info->module->getContext())));
    globalInt->setInitializer(info->irBuilder->getInt32(value));
    globalInt->setConstant(true);
    info->lvt->addGlobalValue(cname, globalInt, GEN_PTR, false);
#endif
  }
}

static void genGlobalInt32(const char *cname, int value) {
  GenInfo *info = gGenInfo;
  if (info->cfile) {
    fprintf(info->cfile, "const int32_t %s = %d;\n", cname, value);
  } else {
#ifdef HAVE_LLVM
    llvm::GlobalVariable *globalInt =
        llvm::cast<llvm::GlobalVariable>(info->module->getOrInsertGlobal(
            cname, llvm::IntegerType::getInt32Ty(info->module->getContext())));
    globalInt->setInitializer(info->irBuilder->getInt32(value));
    globalInt->setConstant(true);
    info->lvt->addGlobalValue(cname, globalInt, GEN_PTR, false);
#endif
  }
}

static bool
isObjectOrSubclass(Type* t)
{
  if (AggregateType* ct = toAggregateType(t))
    if (!isReferenceType(ct) && isClass(ct) &&
        (ct == dtObject || ct->symbol->hasFlag(FLAG_OBJECT_CLASS) ||
         !ct->symbol->hasFlag(FLAG_NO_OBJECT)))
      return true;

  return false;
}

static void
genClassIDs(std::vector<TypeSymbol*> & typeSymbol, bool isHeader) {
  genComment("Class Type Identification Numbers");

  forv_Vec(TypeSymbol, ts, typeSymbol) {
    if (AggregateType* ct = toAggregateType(ts->type)) {
      if (isObjectOrSubclass(ct)) {
        int id = ct->classId;
        INT_ASSERT(id != 0);
        genGlobalDefClassId(ts->cname, id, isHeader);
      }
    }
  }
}

struct compareSymbolFunctor {
  // This is really operator less-than
  bool operator() (const Symbol* a, const Symbol* b) const {
    return compareSymbol(a, b);
  }
};


// Visit class types in depth-first preorder order.
// Assigns class IDs to classes in that order.
static void preorderVisitClassesComputeIds(TypeSymbol* ts, int* nextNumber) {
  typedef std::set<TypeSymbol*, compareSymbolFunctor> children_set;

  children_set children;

  if (ts != NULL) {
    AggregateType* at   = toAggregateType(ts->type);
    int            myN1 = *nextNumber;

    INT_ASSERT(at != NULL);

    *nextNumber = *nextNumber + 1;
    at->classId = myN1;

    // visit children in order
    forv_Vec(AggregateType, child, at->dispatchChildren) {
      if (child != NULL) {
        children.insert(child->symbol);
      }
    }

    for (children_set::iterator it = children.begin();
         it != children.end();
         ++it ) {
      TypeSymbol* child = *it;

      preorderVisitClassesComputeIds(child, nextNumber);
    }
  }
}

static int gMaxClassId = 1;

static void assignClassIds() {
  int next = 1;

  preorderVisitClassesComputeIds(dtObject->symbol, &next);

  gMaxClassId = next - 1;
}


// Computes a maximum ID of subclasses and stores that in n2.
// Returns the maximum ID of a subclass.
// This helps with Schubert numbering
static int computeMaxSubclass(TypeSymbol* ts, std::vector<int>& n2) {
  int retval = 0;

  if (ts != NULL) {
    AggregateType* at    = toAggregateType(ts->type);
    int            myId  = at->classId;
    int            maxN1 = myId;

    forv_Vec(AggregateType, child, at->dispatchChildren) {
      if (child != NULL) {
        int subMax = computeMaxSubclass(child->symbol, n2);

        if (subMax > maxN1) {
          maxN1 = subMax;
        }
      }
    }

    if ((size_t) myId >= n2.size()) {
      n2.resize(myId + 1);
    }

    // set n2 for node, which is max of this n1
    // and child n1s.
    n2[myId] = maxN1;
    retval   = maxN1;
  }

  return retval;
}


static void codegenGlobalConstArray(const char*          name,
                                    const char*          eltType,
                                    std::vector<GenRet>* vals,
                                    bool                 isHeader) {
  GenInfo* info = gGenInfo;

  if(isHeader) {
    if( info->cfile ) {
      FILE* hdrfile = info->cfile;
      fprintf(hdrfile, "extern const %s %s[];\n", eltType, name);
    }
    return;
  }

  // Now generate arrays
  if( info->cfile ) {
    FILE* f = info->cfile;
    fprintf(f, "const %s %s[] = {\n", eltType, name);
    bool first = true;
    std::vector<GenRet> & array = *vals;
    int n = array.size();
    for(int i = 0; i < n; i++ ) {
      if (!first)
        fprintf(f, ",\n");
      fprintf(f, "/* %d */ %s", i, array[i].c.c_str());
      first = false;
    }
    fprintf(f, "\n};\n");
  } else {
#ifdef HAVE_LLVM

  llvm::Type *llvmEltType = getTypeLLVM(eltType);

  INT_ASSERT(llvmEltType);

  std::vector<llvm::Constant *> table;

  std::vector<GenRet> & array = *vals;
  int n = array.size();
  table.resize(n);
  for(int i = 0; i < n; i++ ) {
    llvm::Value* val = array[i].val;
    INT_ASSERT(val);
    table[i] = llvm::cast<llvm::Constant>(val);
  }

  llvm::ArrayType *tableType =
    llvm::ArrayType::get(llvmEltType, table.size());

  if(llvm::GlobalVariable *globalTable = info->module->getNamedGlobal(name)) {
    globalTable->eraseFromParent();
  }

  llvm::GlobalVariable *globalTable =llvm::cast<llvm::GlobalVariable>(
      info->module->getOrInsertGlobal(name, tableType));
  globalTable->setInitializer(llvm::ConstantArray::get(tableType, table));
  globalTable->setConstant(true);

  info->lvt->addGlobalValue(name, globalTable, GEN_VAL, true);
#endif
  }
}

// This uses Schubert Numbering but we could use Cohen's Display,
// which can be computed more incrementally.
// See
// "Implementing statically typed object-oriented programming languages",
// by Roland Ducournau
static void
genSubclassArray(bool isHeader) {
  const char* eltType = "chpl__class_id";
  const char* name = "chpl_subclass_max_id";

  if(isHeader) {
    // Just pass NULL when generating header
    codegenGlobalConstArray(name, eltType, NULL, true);
    return;
  }

  // Otherwise, compute n2 array and then code-generate it
  std::vector<int> n2;

  computeMaxSubclass(dtObject->symbol, n2);

  // make sure n2 always contains at least 1 element
  if (n2.empty())
    n2.push_back(0);

  // Construct the GenRet array of integers
  std::vector<GenRet> tmp;
  for(size_t i = 0; i < n2.size(); i++) {
    tmp.push_back( new_IntSymbol(n2[i], INT_SIZE_32)->codegen() );
  }

  // Now emit the global array declaration
  codegenGlobalConstArray(name, eltType, &tmp, false);
}


// Returns the type, in .c or .type field, for the passed name.
// The type_name typically refers to something defined in the runtime.
static
GenRet codegenTypeByName(const char* type_name)
{
  GenInfo* info = gGenInfo;

  GenRet ret;
  if (info->cfile) {
    ret.c = type_name;
  } else {
#ifdef HAVE_LLVM
    ret.type = getTypeLLVM(type_name);
#endif
  }
  return ret;
}



// codegenTypedNull takes in the pointer type
// (.c string or .type for LLVM) and generates NULL of that type.
static
GenRet codegenTypedNull(GenRet funcPtrType)
{
  GenInfo* info = gGenInfo;

  GenRet nullFn;
  if (info->cfile) {
    // C doesn't really care about the type of NULL, so use existing routine.
    nullFn.c = "(" + funcPtrType.c + ")(NULL)";
  } else {
#ifdef HAVE_LLVM
    // With LLVM, generate a NULL of the right type.
    INT_ASSERT(funcPtrType.type);
    nullFn.val = llvm::Constant::getNullValue(funcPtrType.type);
#endif
  }
  return nullFn;
}

#ifdef HAVE_LLVM
static
llvm::Constant* codegenStringForTableLLVM(std::string s)
{
  llvm::Constant* ret;
  GenRet str = new_CStringSymbol(s.c_str())->codegen();
  ret = llvm::cast<llvm::GlobalVariable>(str.val)->getInitializer();
  return ret;
}
#endif

static
GenRet codegenStringForTable(std::string s)
{
  GenInfo *info = gGenInfo;

  GenRet ret;
  if (info->cfile) {
    ret.c = "\"" + s + "\"";
  } else {
#ifdef HAVE_LLVM
    ret.val = codegenStringForTableLLVM(s);
#endif
  }
  return ret;
}


static void
genFtable(std::vector<FnSymbol*> & fSymbols, bool isHeader) {
  GenInfo* info = gGenInfo;

  const char* eltType = "chpl_fn_p";
  const char* name = "chpl_ftable";

  if (isHeader) {
    // Just pass NULL when generating header
    codegenGlobalConstArray(name, eltType, NULL, true);
    return;
  }

  if (gCodegenGPU == true) {
    return;
  }

  GenRet funcPtrType = codegenTypeByName(eltType);

  // Construct the table elements
  std::vector<GenRet> ftable;
  ftable.reserve(fSymbols.size());

  forv_Vec(FnSymbol, fn, fSymbols) {
    GenRet gen;
    if (info->cfile) {
      gen.c = "(" + funcPtrType.c + ")";
      gen.c += fn->cname;
    } else {
#ifdef HAVE_LLVM
      INT_ASSERT(funcPtrType.type);
      llvm::Function *func = getFunctionLLVM(fn->cname);
      gen.val = info->irBuilder->CreatePointerCast(func, funcPtrType.type);
#endif
    }
    ftable.push_back(gen);
  }

  // make sure ftable always contains at least 1 element
  if (ftable.empty()) {
    GenRet nullFn = codegenTypedNull(funcPtrType);
    ftable.push_back(nullFn);
  }

  // Now emit the global array declaration
  codegenGlobalConstArray(name, eltType, &ftable, false);
}

static void
genFinfo(std::vector<FnSymbol*> & fSymbols, bool isHeader) {
  GenInfo* info = gGenInfo;

  const char* eltType = "chpl_fn_info";
  const char* name = "chpl_finfo";

  if(isHeader) {
    // Just pass NULL when generating header
    codegenGlobalConstArray(name, eltType, NULL, true);
    return;
  }

  // Compute the element type
  GenRet structType = codegenTypeByName(eltType);

#ifdef HAVE_LLVM
  llvm::Type *int32Ty = NULL;
  if (!info->cfile) {
    int32Ty = llvm::IntegerType::getInt32Ty(info->module->getContext());
  }
#endif


  // Construct the table elements
  std::vector<GenRet> finfo;
  finfo.reserve(fSymbols.size());

  // buf for creating C structures
  char* buf = NULL;
  int buf_len = 0;

  if (info->cfile) {
    // compute the maximum file name length
    forv_Vec(FnSymbol, fn, fSymbols) {
      int len = strlen(fn->cname);
      if (len > buf_len)
        buf_len = len;
    }
    // and then add 100 for two integers and punctuation
    buf_len += 100;
    buf = (char*) malloc(buf_len);
  }

  forv_Vec(FnSymbol, fn, fSymbols) {
    const char* fn_name = fn->cname;
    int fileno = getFilenameLookupPosition(fn->astloc.filename());
    int lineno = fn->astloc.lineno();

    GenRet gen;

    if (info->cfile) {
      int rc = snprintf(buf, buf_len,
                        "{\"%s\", %d, %d}", fn_name, fileno, lineno);
      INT_ASSERT( rc < buf_len ); // assert output not truncated
      gen.c = buf;
    } else {
#ifdef HAVE_LLVM
      llvm::Constant* fields[3];
      fields[0] = codegenStringForTableLLVM(fn_name);
      fields[1] = llvm::ConstantInt::get(int32Ty, fileno);
      fields[2] = llvm::ConstantInt::get(int32Ty, lineno);
      INT_ASSERT(structType.type);
      llvm::StructType* st = llvm::cast<llvm::StructType>(structType.type);
      gen.val = llvm::ConstantStruct::get(st, fields);
#endif
    }

    finfo.push_back(gen);
  }

  // Free the buffer for C conversions.
  if (buf) free(buf);

  // make sure the table always contains at trailing NULL element
  {
    GenRet nullStruct;
    if (info->cfile) {
      nullStruct.c = "{(char *)0, 0, 0}";
    } else {
      nullStruct = codegenTypedNull(structType);
    }
    finfo.push_back(nullStruct);
  }

  // Now emit the global array declaration
  codegenGlobalConstArray(name, eltType, &finfo, false);
}

static void
genVirtualMethodTable(std::vector<TypeSymbol*>& types, bool isHeader) {
  GenInfo* info = gGenInfo;
  const char* vmt = "chpl_vmtable";
  const char* eltType = "chpl_fn_p";
  if(isHeader) {
    codegenGlobalConstArray(vmt, eltType, NULL, true);
    return;
  }

  // compute max # methods per type
  int maxVMT = 0;

  // note: the virtual method table can contain keys
  // that point to deallocated memory (e.g. for classes that
  // have been removed). So it is important to only 'get'
  // live AST elements from the VMT rather than traversing it
  // directly.
  forv_Vec(TypeSymbol, ts, types) {
    if (AggregateType* ct = toAggregateType(ts->type))
      if (isObjectOrSubclass(ct))
        if (Vec<FnSymbol*>* vfns = virtualMethodTable.get(ct))
          if (vfns->n > maxVMT)
            maxVMT = vfns->n;
  }
  gMaxVMT = maxVMT;

  GenRet funcPtrType = codegenTypeByName(eltType);

  std::vector<GenRet> vmt_elts;

  // Make sure VMT has at least one element
  vmt_elts.resize(1);

  // compute 1D virtual method table
  // (this is not fundamental, but is currently used to simplify codegen)
  //    indexExpr = maxVMT * classId + fnId
  forv_Vec(TypeSymbol, ts, types) {
    if (AggregateType* ct = toAggregateType(ts->type)) {
      if (isObjectOrSubclass(ct)) {
        if (Vec<FnSymbol*>* vfns = virtualMethodTable.get(ct)) {
          int i = 0;
          forv_Vec(FnSymbol, vfn, *vfns) {
            if (vfn->hasFlag(FLAG_GPU_CODEGEN) == gCodegenGPU) {
              int classId = ct->classId;
              int fnId = i;
              int index = gMaxVMT * classId + fnId;

              INT_ASSERT(classId > 0);

              GenRet fnAddress;

              if( info->cfile ) {
                fnAddress.c = "(" + funcPtrType.c + ")";
                fnAddress.c += vfn->cname;
                if (fGenIDS) {
                  fnAddress.c += " /* ";
                  fnAddress.c += std::to_string(vfn->id);
                  fnAddress.c += " */";
                }
              } else {
#ifdef HAVE_LLVM
                INT_ASSERT(funcPtrType.type);
                llvm::Function *func = getFunctionLLVM(vfn->cname);
                fnAddress.val = info->irBuilder->CreatePointerCast(func, funcPtrType.type);
#endif
              }

              if (vmt_elts.size() <= (size_t) index)
                vmt_elts.resize(index+1);

              vmt_elts[index] = fnAddress;

              i++;
            }
          }
        }
      }
    }
  }

  // Fill any elements not filled above with codegenNullPointer
  for (size_t i = 0; i < vmt_elts.size(); i++) {
    if (vmt_elts[i].isEmpty()) {
      vmt_elts[i] = codegenTypedNull(funcPtrType);
    }
  }


  codegenGlobalConstArray(vmt, eltType, &vmt_elts, false);
}

static void genFilenameTable() {
  const char *name = "chpl_filenameTable";
  const char *sizeName = "chpl_filenameTableSize";
  const char *eltType = "c_string";

  // Compute the element type
  GenRet cstringType = codegenTypeByName(eltType);

  // Construct the table elements
  std::vector<GenRet> table;
  table.reserve(gFilenameLookup.size());

  for (std::vector<std::string>::iterator it = gFilenameLookup.begin();
       it != gFilenameLookup.end(); it++) {
    GenRet gen;
    std::string & path = (*it);
    std::string genPath;

    if(!strncmp(CHPL_HOME, path.c_str(), strlen(CHPL_HOME))) {
      genPath = "$CHPL_HOME";
      genPath += (path.c_str()+strlen(CHPL_HOME));
    } else {
      genPath = path;
    }

    gen = codegenStringForTable(genPath);
    table.push_back(gen);
  }

  // Now emit the global array declaration
  codegenGlobalConstArray(name, eltType, &table, false);

  // Now emit the size
  genGlobalInt32(sizeName, gFilenameLookup.size());
}

//
// This adds the Chapel symbol table to the config file
// Our symbol table is formed by two 1-D arrays with 2 elements
// per entry:
//
// chpl_funSymTable     = cname, Chapel name
// chpl_filenumSymTable = Chapel file name index, Chapel line number
//
static void genUnwindSymbolTable(){
  std::vector<FnSymbol*> symbols;

  //If CHPL_UNWIND is none we don't want any symbols in our tables
  if(strcmp(CHPL_UNWIND, "none") != 0){
    // Gets only user symbols
    forv_Vec(FnSymbol, fn, gFnSymbols) {
      if(strncmp(fn->name, "chpl_", 5) || fn->hasFlag(FLAG_MODULE_INIT)) {
        symbols.push_back(fn);
      }
    }
  }

  // Generate the cname, Chapel name table
  {
    const char *name = "chpl_funSymTable";
    const char *eltType = "c_string";

    // Compute the element type
    GenRet cstringType = codegenTypeByName(eltType);

    // Construct the table elements
    std::vector<GenRet> table;
    table.reserve(symbols.size() * 2);

    for (FnSymbol* fn : symbols) {
      table.push_back(codegenStringForTable(fn->cname));
      table.push_back(codegenStringForTable(fn->name));
    }
    table.push_back(codegenStringForTable(""));
    table.push_back(codegenStringForTable(""));

    // Now emit the global array declaration
    codegenGlobalConstArray(name, eltType, &table, false);
  }

  // Generate the filename index, linenum table
  {
    const char *name = "chpl_filenumSymTable";
    const char *eltType = "c_int";

    // Compute the element type
    GenRet cintType = codegenTypeByName(eltType);

    // Construct the table elements
    std::vector<GenRet> table;
    table.reserve(symbols.size() * 2);

    for (FnSymbol* fn : symbols) {
      int fileno = getFilenameLookupPosition(fn->fname());
      int lineno = fn->linenum();

      table.push_back( new_IntSymbol(fileno, INT_SIZE_32)->codegen() );
      table.push_back( new_IntSymbol(lineno, INT_SIZE_32)->codegen() );
    }
    table.push_back( new_IntSymbol(0, INT_SIZE_32)->codegen() );
    table.push_back( new_IntSymbol(0, INT_SIZE_32)->codegen() );

    // Now emit the global array declaration
    codegenGlobalConstArray(name, eltType, &table, false);
  }

  // Now emit the size of the symbol table
  genGlobalInt32("chpl_sizeSymTable", symbols.size() * 2);
}

static void
genClassNames(std::vector<TypeSymbol*> & typeSymbol, bool isHeader) {
  const char* eltType = "c_string";
  const char* name = "chpl_classNames";

  if(isHeader) {
    // Just pass NULL when generating header
    codegenGlobalConstArray(name, eltType, NULL, true);
    return;
  }

  std::vector<const char*> names;

  forv_Vec(TypeSymbol, ts, typeSymbol) {
    if (AggregateType* ct = toAggregateType(ts->type)) {
      if (isObjectOrSubclass(ct)) {
        int id = ct->classId;
        INT_ASSERT(id > 0);
        if (id >= (int)names.size())
          names.resize(id+1, NULL);
        names[id] = ts->name;
      }
    }
  }

  std::vector<GenRet> tmp;
  for(size_t i = 0; i < names.size(); i++) {
    const char* name = names[i];
    if (name == NULL)
      name = "";
    tmp.push_back(codegenStringForTable(name));
  }

  // Now emit the global array declaration
  codegenGlobalConstArray(name, eltType, &tmp, false);
}


static bool
compareSymbol(const void* v1, const void* v2) {
  Symbol* s1 = (Symbol*)v1;
  Symbol* s2 = (Symbol*)v2;
  ModuleSymbol* m1 = s1->getModule();
  ModuleSymbol* m2 = s2->getModule();
  if (m1 != m2) {
    if (m1->modTag < m2->modTag)
      return 1;
    if (m1->modTag > m2->modTag)
      return 0;
    return strcmp(m1->cname, m2->cname) < 0;
  }

  // prefer to place externs earlier in the function list (vector)
  // this was necessary because in the new parser the order in which
  // extern and non-externs are identified does not match the old parser.
  // this keeps things consistent between the old and new parser
  if (s1->hasFlag(FLAG_EXTERN) != s2->hasFlag(FLAG_EXTERN)) {
    if (s1->hasFlag(FLAG_EXTERN))
      return 1;
    return 0;
  }

  if (s1->linenum() != s2->linenum())
    return s1->linenum() < s2->linenum();

  int result = strcmp(s1->type->symbol->cname, s2->type->symbol->cname);
  if (!result)
    result = strcmp(s1->cname, s2->cname);

  return result < 0;
}

static int
compareSymbol2(const void* v1, const void* v2) {
  Symbol* s1 = *(Symbol* const *)v1;
  Symbol* s2 = *(Symbol* const *)v2;
  ModuleSymbol* m1 = s1->getModule();
  ModuleSymbol* m2 = s2->getModule();
  if (m1 != m2) {
    if (m1->modTag < m2->modTag)
      return -1;
    if (m1->modTag > m2->modTag)
      return 1;
    return strcmp(m1->cname, m2->cname);
  }

  if (s1->linenum() != s2->linenum())
    return (s1->linenum() < s2->linenum()) ? -1 : 1;

  int result = strcmp(s1->type->symbol->cname, s2->type->symbol->cname);
  if (!result)
    result = strcmp(s1->cname, s2->cname);
  return result;
}

//
// given a name and up to two sets of names, return a name that is in
// neither set and add the name to the first set; the second set may
// be omitted; the returned name to be capped at fMaxCIdentLen if non-0
// less how much can be added to it - maxCNameAddedChars
//
// the unique numbering is based on the map uniquifyNameCounts which
// can be cleared to reset
//
int fMaxCIdentLen = 0;
static const int maxUniquifyAddedChars = 25;
// keep in sync with AggregateType::classStructName()
static const int maxCNameAddedChars = 20;
static char* longCNameReplacementBuffer = NULL;
static Map<const char*, int> uniquifyNameCounts;

// Return the next uniquifying number for 'name'.
// Cache the lookup result in 'elem' - speed up "call_tmp" etc.
static int uniquifyNameNextCount(MapElem<const char*, int>*& elem,
                                 const char* name) {
  if (!elem) {
    elem = uniquifyNameCounts.get_record(name);
    if (!elem) {  // The first time we see 'name'.
      uniquifyNameCounts.put(name, 2);
      return 2;
    }
  }
  return ++elem->value;
}

static void uniquifyName(Symbol* sym,
                         std::set<const char*>* set1,
                         std::set<const char*>* set2 = NULL)
{
  const char* name = sym->cname;
  const char* newName = name;

  if (sym->isRenameable())
  {
    if (fMaxCIdentLen > 0 &&
        (int)(strlen(newName) + maxCNameAddedChars) > fMaxCIdentLen)
    {
      // how much of the name to preserve
      int prefixLen = fMaxCIdentLen - maxUniquifyAddedChars - maxCNameAddedChars;
      if (!longCNameReplacementBuffer) {
        longCNameReplacementBuffer = (char*)malloc(prefixLen+1);
        longCNameReplacementBuffer[prefixLen] = '\0';
      }
      strncpy(longCNameReplacementBuffer, newName, prefixLen);
      INT_ASSERT(longCNameReplacementBuffer[prefixLen] == '\0');
      longCNameReplacementBuffer[prefixLen-1] = 'X'; //fyi truncation marker
      name = newName = astr(longCNameReplacementBuffer);
    }

    MapElem<const char*, int>* elem = NULL;
    while ((set1->find(newName)!=set1->end()) || (set2 && (set2->find(newName)!=set2->end()))) {
      char numberTmp[64];
      snprintf(numberTmp, 64, "%d", uniquifyNameNextCount(elem, name));
      newName = astr(name, numberTmp);
    }

    sym->cname = newName;
  }
  else
  {
    // If we have already seen this name before, we need to go back
    // to that earlier-seen symbol, either renaming it or checking
    // that its type is the same. See also #9299.
    //
    // This is currently not implemented. For now, at least detect it
    // to avoid generating erroneous C code silently.
    //
    // Do this only for things local to a function.
    // We use multiple Symbols for the same extern at the global scope,
    // be it a (possibly generic) function, type, variable.
    // Ex.: c_pointer_return, qbuffer_ptr_t, QBUFFER_PTR_NULL.
    //
    if (set2 && (set1->find(name) != set1->end()) )
      INT_FATAL(sym, "name conflict with a non-renameable symbol");
  }

  // Record the name even if the symbol is not renameable.
  // This enables detection of like-named symbols encountered later.
  set1->insert(newName);
}

static inline bool shouldCodegenAggregate(AggregateType* ct)
{
  // never codegen definitions of primitive or arithmetic types.
  if( toPrimitiveType(ct) ) return false;

  // Don't codegen types with FLAG_NO_CODEGEN.  This is used for
  // types that are defined in the runtime for example.
  if( ct->symbol->hasFlag(FLAG_NO_CODEGEN) ) return false;

  // Don't visit classes since they are prototyped individually all at once..
  // ..except for classes with FLAG_REF or FLAG_DATA_CLASS.. which
  //   we do visit.
  if( isClass(ct) ) { // is it actually a class?
    if( ct->symbol->hasFlag(FLAG_REF) ||
        ct->symbol->hasFlag(FLAG_WIDE_REF) ||
        ct->symbol->hasFlag(FLAG_DATA_CLASS)) return true;
    else return false;
  }

  // otherwise, visit record/union
  return true;
}


static void codegen_aggregate_def(AggregateType* ct) {
  //DFS, check visited
  if (!shouldCodegenAggregate(ct)) return;
  if (ct->symbol->hasFlag(FLAG_CODEGENNED)) return;
  ct->symbol->addFlag(FLAG_CODEGENNED);

  // For reference or data class types, first generate
  // the referenced type
  Type* vt = NULL;
  if(ct->symbol->hasFlag(FLAG_REF))
    vt = ct->symbol->getValType();
  else if(ct->symbol->hasFlag(FLAG_DATA_CLASS))
    vt = getDataClassType(ct->symbol)->typeInfo();
  else if(ct->symbol->hasFlag(FLAG_C_ARRAY))
    vt = ct->cArrayElementType();
  if (vt) {
    if (AggregateType* fct = toAggregateType(vt)) {
      codegen_aggregate_def(fct);
    }
  }
  // For other types, generate the field types
  for_fields(field, ct) {
    if (AggregateType* fct = toAggregateType(field->type)) {
      codegen_aggregate_def(fct);
    }
  }
  // Lastly, generate the type we're working on.
  // Codegen what we have here.
  ct->symbol->codegenDef();
}

static void genConfigGlobalsAndAbout() {
  GenInfo* info = gGenInfo;

  if (info->cfile) {
    genComment("Compilation Info");
    fprintf(info->cfile, "\n#include <stdio.h>");
    fprintf(info->cfile, "\n#include \"chpltypes.h\"\n\n");
  }

  genGlobalString("chpl_compileCommand", compileCommand);
  genGlobalString("chpl_compileVersion", compileVersion);
  genGlobalString("chpl_compileDirectory", getCwd());
  if (strcmp(saveCDir, "") != 0) {
    char *actualPath = realpath(saveCDir, NULL);
    genGlobalString("chpl_saveCDir", actualPath);
  } else {
    genGlobalString("chpl_saveCDir", "");
  }

  genGlobalString("CHPL_HOME", CHPL_HOME);

  genGlobalInt("CHPL_STACK_CHECKS", !fNoStackChecks, false);
  genGlobalInt("CHPL_CACHE_REMOTE", fCacheRemote, false);
  genGlobalInt("CHPL_INTERLEAVE_MEM", fEnableMemInterleaving, false);

  for (std::map<std::string, const char*>::iterator env=envMap.begin(); env!=envMap.end(); ++env) {
    if (env->first != "CHPL_HOME") {
      genGlobalString(env->first.c_str(), env->second);
    }
  }

  if (info->cfile) {
    fprintf(info->cfile, "\nvoid chpl_program_about(void);\n");
    fprintf(info->cfile, "\nvoid chpl_program_about() {\n");
  } else {
#ifdef HAVE_LLVM
    llvm::FunctionType* programAboutType;
    llvm::Function* programAboutFunc;
    if ((programAboutFunc = getFunctionLLVM("chpl_program_about"))) {
      programAboutType = programAboutFunc->getFunctionType();
    } else {
      programAboutType = llvm::FunctionType::get(
        llvm::Type::getVoidTy(info->module->getContext()), false
      );
      programAboutFunc = llvm::Function::Create(
        programAboutType, llvm::Function::ExternalLinkage, "chpl_program_about", info->module
      );
    }

    llvm::BasicBlock* programAboutBlock = llvm::BasicBlock::Create(
      info->module->getContext(), "entry", programAboutFunc
    );
    info->irBuilder->SetInsertPoint(programAboutBlock);
#endif
  }

  codegenCallPrintf(astr("Compilation command: ", compileCommand, "\\n"));
  codegenCallPrintf(astr("Chapel compiler version: ", compileVersion, "\\n"));
  codegenCallPrintf("Chapel environment:\\n");
  codegenCallPrintf(astr("  CHPL_HOME: ", CHPL_HOME, "\\n"));
  for (std::map<std::string, const char*>::iterator env=envMap.begin(); env!=envMap.end(); ++env) {
    if (env->first != "CHPL_HOME") {
      codegenCallPrintf(astr("  ", env->first.c_str(), ": ", env->second, "\\n"));
    }
  }

  if (info->cfile) {
    fprintf(info->cfile, "}\n");
  } else {
#ifdef HAVE_LLVM
    info->irBuilder->CreateRetVoid();
#endif
  }
}

static void genFunctionTables() {
  genComment("Filename Lookup Table");
  genFilenameTable();

  genComment("Unwind symbol tables");
  genUnwindSymbolTable();
}

//
// Produce compilation-time configuration info into a .c file and
// #include that .c into the current codegen output file.
//
// Only put C data objects into this file, not Chapel ones, as it may
// also be #include'd into a launcher, and those are C/C++ code.
//
// New generated variables should be added to runtime/include/chplcgfns.h
//
static const char* sCfgFname = "chpl_compilation_config";

static void codegen_header_compilation_config() {
  const bool usingLauncher = 0 != strcmp(CHPL_LAUNCHER, "none");
  // Generate C code only when not in LLVM mode or when using a launcher
  const bool genCCode = usingLauncher || !fLlvmCodegen;

  GenInfo* info = gGenInfo;
  FILE* save_cfile = info->cfile;
  fileinfo cfgfile = { NULL, NULL, NULL };

  if (fLlvmCodegen) {
    info->cfile = NULL;
    if ( gCodegenGPU == false ) {
      genConfigGlobalsAndAbout();
      genFunctionTables();
    }
  }

  // Generate the about info and function tables for the C backend and for the launcher
  if (genCCode) {
    openCFile(&cfgfile, sCfgFname, "c");
    // Follow convention of just not writing to the file if we can't open it
    if (cfgfile.fptr) {
      info->cfile = cfgfile.fptr;
      genConfigGlobalsAndAbout();
      genFunctionTables();
      closeCFile(&cfgfile);
    }
  }

  info->cfile = save_cfile;
}

static void protectNameFromC(Symbol* sym) {
  //
  // Symbols that start with 'chpl_' were presumably named by the
  // implementation (compiler, internal modules, runtime) and
  // sufficiently unique to not require further munging.
  //
  if (strncmp(sym->cname, "chpl_", 5) == 0) {
    return;
  }

  //
  // Don't rename the symbol if it's not able to be (typically because
  // it's exported, extern, or has otherwise been flagged as not being
  // renameable).
  //
  if (!sym->isRenameable()) {
    return;
  }

  // Don't rename fields
  if (isVarSymbol(sym)) {
    if (isAggregateType(sym->defPoint->parentSymbol->type)) {
      return;
    }
  }

  //
  // Walk from the symbol up to its enclosing module.  If the symbol
  // is declared within an extern declaration, we should preserve its
  // name for similar reasons.
  //
  ModuleSymbol* symMod = sym->getModule();
  if (sym != symMod) {
    Symbol* parentSym = sym->defPoint->parentSymbol;
    while (parentSym != symMod) {
      if (parentSym->hasFlag(FLAG_EXTERN)) {
        return;
      }
      parentSym = parentSym->defPoint->parentSymbol;
    }
  }

  //
  // For the sake of clarity, let's also avoid renaming arguments of
  // exported functions.
  //
  if (toArgSymbol(sym)) {
    Symbol* parentSym = sym->defPoint->parentSymbol;
    if (parentSym->hasFlag(FLAG_EXPORT)) {
      return;
    }
  }

  //
  // Rename the symbol
  //
  const char* oldName = sym->cname;
  const char* newName = astr(oldName, "_chpl");
  sym->cname = newName;
  //
  // Can we free this given how we create names?  free() doesn't like
  // const char*, I don't want to just cast it away, and I'm not
  // certain we can assume it isn't aliased to something else, like
  // sym->name...  In other cases, we seem to leak old names as
  // well... :P
  //
  //  free(oldName);
}

static void genGlobalSerializeTable(GenInfo* info) {
  FILE* hdrfile = info->cfile;
  std::vector<CallExpr*> serializeCalls;
  forv_Vec(CallExpr, call, gCallExprs) {
    if (call->isResolved() && call->resolvedFunction()->hasFlag(FLAG_BROADCAST_FN)) {
      SymExpr* se = toSymExpr(call->get(2));
      INT_ASSERT(se != NULL);

      VarSymbol* imm = toVarSymbol(se->symbol());
      INT_ASSERT(imm && imm->isImmediate());
      uint64_t idx = imm->immediate->int_value();

      if (idx+1 > serializeCalls.size()) {
        serializeCalls.resize(idx+1);
      }

      serializeCalls[idx] = call;
    }
  }

  if( hdrfile ) {
    fprintf(hdrfile, "\nvoid* const chpl_global_serialize_table[] = {");
    if (serializeCalls.size() == 0) {
      // Quiet PGI warning about empty initializer
      fprintf(hdrfile, "\nNULL,");
    } else {
      for (unsigned int i = 0; i < serializeCalls.size(); i++) {
        CallExpr* call = serializeCalls[i];
        INT_ASSERT(call != NULL);
        SymExpr* global = toSymExpr(call->get(1));
        INT_ASSERT(isModuleSymbol(global->symbol()->defPoint->parentSymbol));

        const char* prefix = i == 0 ? "\n&%s" : ",\n&%s";
        fprintf(hdrfile, prefix, global->symbol()->cname);
      }
    }
    fprintf(hdrfile, "\n};\n");
  } else {
#ifdef HAVE_LLVM
    llvm::Type *global_serializeTableEntryType =
      llvm::IntegerType::getInt8PtrTy(info->module->getContext());

    std::vector<llvm::Constant *> global_serializeTable;

    for_vector(CallExpr, call, serializeCalls) {
      SymExpr* se = toSymExpr(call->get(1));
      INT_ASSERT(se);

      global_serializeTable.push_back(llvm::cast<llvm::Constant>(
            info->irBuilder->CreatePointerCast(
              info->lvt->getValue(se->symbol()->cname).val,
              global_serializeTableEntryType)));
    }

    if(llvm::GlobalVariable *GVar = llvm::cast_or_null<llvm::GlobalVariable>(
          info->module->getNamedGlobal("chpl_global_serialize_table"))) {
      GVar->eraseFromParent();
    }

    llvm::ArrayType *global_serializeTableType =
      llvm::ArrayType::get(global_serializeTableEntryType,
                          global_serializeTable.size());
    llvm::GlobalVariable *global_serializeTableGVar =
      llvm::cast<llvm::GlobalVariable>(
          info->module->getOrInsertGlobal("chpl_global_serialize_table",
                                          global_serializeTableType));
    global_serializeTableGVar->setInitializer(
        llvm::ConstantArray::get(
          global_serializeTableType, global_serializeTable));
    info->lvt->addGlobalValue("chpl_global_serialize_table",
                              global_serializeTableGVar, GEN_PTR, true);
#endif
  }
}

// TODO: Split this into a number of smaller routines.<hilde>
static void codegen_defn(std::set<const char*> & cnames, std::vector<TypeSymbol*> & types,
  std::vector<FnSymbol*> & functions, std::vector<VarSymbol*> & globals) {
  GenInfo* info = gGenInfo;
  FILE* hdrfile = info->cfile;

  genClassIDs(types, false);
  genSubclassArray(false);
  genClassNames(types, false);

  genComment("Function Pointer Table");
  genFtable(ftableVec, false);
  genFinfo(ftableVec, false);

  genComment("Virtual Method Table");
  genVirtualMethodTable(types, false);

  if(fIncrementalCompilation) {
    genComment("Global Variables");
    forv_Vec(VarSymbol, varSymbol, globals) {
      varSymbol->codegenGlobalDef(false);
    }
  }

  flushStatements();
#ifndef HAVE_LLVM
  zlineToFileIfNeeded(rootModule, info->cfile);
#endif

  genComment("Global Serialize Table");
  genGlobalSerializeTable(info);

  genGlobalInt("chpl_numGlobalsOnHeap", numGlobalsOnHeap, false);
  int globals_registry_static_size = (numGlobalsOnHeap ? numGlobalsOnHeap : 1);
  if( hdrfile ) {
    fprintf(hdrfile, "\nptr_wide_ptr_t chpl_globals_registry[%d];\n",
                      globals_registry_static_size);
  } else {
    #ifdef HAVE_LLVM
          return; // Nothing in remainder of function should be done twice for LLVM
    #endif
  }
  if( hdrfile ) {
    fprintf(hdrfile, "\nconst char* chpl_mem_descs[] = {\n");
    bool first = true;
    if (memDescsVec.n == 0) {
      // Quiet PGI warning about empty initializer
      fprintf(hdrfile, "\nNULL,");
    } else {
      forv_Vec(const char*, memDesc, memDescsVec) {
        if (!first)
          fprintf(hdrfile, ",\n");
        fprintf(hdrfile, "\"%s\"", memDesc);
        first = false;
      }
    }
    fprintf(hdrfile, "\n};\n");
  }

  genGlobalInt("chpl_mem_numDescs", memDescsVec.n, false);

  //
  // add table of private-broadcast constants
  //
  if( hdrfile ) {
    fprintf(hdrfile, "\nvoid* const chpl_private_broadcast_table[] = {\n");
    int i = 0;
    forv_Vec(CallExpr, call, gCallExprs) {
      if (call->isPrimitive(PRIM_PRIVATE_BROADCAST)) {
        SymExpr* se = toSymExpr(call->get(1));
        INT_ASSERT(se);
        SET_LINENO(call);
        fprintf(hdrfile, "%s&%s",
                ((i == 0) ? "" : ",\n"), se->symbol()->cname);
        // To preserve operand order, this should be insertAtTail.
        // The change must also be made below (for LLVM) and in the signature
        // of chpl_comm_broadcast_private().
        call->insertAtHead(new_IntSymbol(i));
        i++;
      }
    }
    if (i == 0) {
      // Quiet PGI warning about empty initializer
      fprintf(hdrfile, "NULL");
    }
    fprintf(hdrfile, "\n};\n");
    genGlobalInt("chpl_private_broadcast_table_len", i, false);
  }
}

static void uniquify_names(std::set<const char*> & cnames,
                           std::vector<TypeSymbol*> & types,
                           std::vector<FnSymbol*> & functions,
                           std::vector<VarSymbol*> & globals) {
  // reserved symbol names that require renaming to compile
#include "reservedSymbolNames.h"

  //
  // collect types and apply canonical sort
  //
  forv_Vec(TypeSymbol, ts, gTypeSymbols) {
    if (ts->defPoint->parentExpr != rootModule->block) {
      legalizeSymbolName(ts);
      types.push_back(ts);
    }
  }
  std::sort(types.begin(), types.end(), compareSymbol);

  //
  // collect globals and apply canonical sort
  //
  forv_Vec(VarSymbol, var, gVarSymbols) {
    if (var->defPoint->parentExpr != rootModule->block &&
        toModuleSymbol(var->defPoint->parentSymbol)) {
      legalizeSymbolName(var);
      globals.push_back(var);
    }
  }
  std::sort(globals.begin(), globals.end(), compareSymbol);
  //
  // collect functions and apply canonical sort
  //
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    legalizeSymbolName(fn);
    functions.push_back(fn);
  }
  std::sort(functions.begin(), functions.end(), compareSymbol);


  //
  // by default, mangle all Chapel symbols to avoid clashing with C
  // identifiers.  To disable, compile with --no-munge-user-idents
  //
  if (fMungeUserIdents) {
    forv_Vec(ModuleSymbol, sym, gModuleSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(VarSymbol, sym, gVarSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(ShadowVarSymbol, sym, gShadowVarSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(ArgSymbol, sym, gArgSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(TypeSymbol, sym, gTypeSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(FnSymbol, sym, gFnSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(EnumSymbol, sym, gEnumSymbols) {
      protectNameFromC(sym);
    }
    forv_Vec(LabelSymbol, sym, gLabelSymbols) {
      protectNameFromC(sym);
    }
  }


  //
  // mangle type names if they clash with other types
  //
  forv_Vec(TypeSymbol, ts, types) {
    uniquifyName(ts, &cnames);
  }
  uniquifyNameCounts.clear();

  //
  // change enum constant names into <type name>_<constant name> and
  // mangle if they clash with other types or enum constants
  //
  forv_Vec(TypeSymbol, ts, types) {
    if (EnumType* enumType = toEnumType(ts->type)) {
      for_enums(constant, enumType) {
        Symbol* sym = constant->sym;
        legalizeSymbolName(sym);
        sym->cname = astr(enumType->symbol->cname, "_", sym->cname);
        uniquifyName(sym, &cnames);
      }
    }
  }
  uniquifyNameCounts.clear();

  //
  // mangle field names if they clash with other fields in the same
  // class
  //
  forv_Vec(TypeSymbol, ts, types) {
    if (ts->defPoint->parentExpr != rootModule->block) {
      if (AggregateType* ct = toAggregateType(ts->type)) {
        std::set<const char*> fieldNameSet;
        for_fields(field, ct) {
          legalizeSymbolName(field);
          uniquifyName(field, &fieldNameSet);
        }
        uniquifyNameCounts.clear();
      }
    }
  }

  //
  // mangle global variable names if they clash with types, enum
  // constants, or other global variables
  //
  forv_Vec(VarSymbol, var, globals) {
    uniquifyName(var, &cnames);
  }
  uniquifyNameCounts.clear();

  //
  // mangle function names if they clash with types, enum constants,
  // global variables, or other functions
  //
  for_vector(FnSymbol, fn, functions) {
    uniquifyName(fn, &cnames);
  }
  uniquifyNameCounts.clear();

  //
  // mangle formal argument names if they clash with types, enum
  // constants, global variables, functions, or earlier formal
  // arguments in the same function
  //
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    std::set<const char*> formalNameSet;
    for_formals(formal, fn) {
      legalizeSymbolName(formal);
      uniquifyName(formal, &formalNameSet, &cnames);
    }
    uniquifyNameCounts.clear();
  }

  //
  // mangle local variable names if they clash with types, global
  // variables, functions, formal arguments of their function, or
  // other local variables in the same function
  //
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    std::set<const char*> local;

    for_formals(formal, fn) {
      local.insert(formal->cname);
    }

    std::vector<DefExpr*> defs;
    collectDefExprs(fn->body, defs);
    for_vector(DefExpr, def, defs) {
      legalizeSymbolName(def->sym);
      // give temps cnames
      if (def->sym->hasFlag(FLAG_TEMP)) {
        if (localTempNames) {
          // temp name is _tNNN_
          if (!strncmp(def->sym->cname, "_t", 2))
            def->sym->cname = astr("T", def->sym->cname + 2);
        } else {
          // temp name is tmp
          if (!strcmp(def->sym->cname, "tmp"))
            def->sym->cname = astr("T");
        }
      }
      uniquifyName(def->sym, &local, &cnames);
    }
    uniquifyNameCounts.clear();
  }
}

static void codegen_header(std::set<const char*> & cnames,
                           std::vector<TypeSymbol*> & types,
                           std::vector<FnSymbol*> & functions,
                           std::vector<VarSymbol*> & globals) {
  GenInfo* info = gGenInfo;

  //
  // collect types and apply canonical sort
  //
  forv_Vec(TypeSymbol, ts, gTypeSymbols) {
    if (ts->defPoint->parentExpr != rootModule->block) {
      types.push_back(ts);
    }
  }
  std::sort(types.begin(), types.end(), compareSymbol);

  //
  // collect globals and apply canonical sort
  //
  forv_Vec(VarSymbol, var, gVarSymbols) {
    if (var->defPoint->parentExpr != rootModule->block &&
        toModuleSymbol(var->defPoint->parentSymbol)) {
      if ( var->hasFlag(FLAG_GPU_CODEGEN) == gCodegenGPU ){
        globals.push_back(var);
      }
    }
  }
  std::sort(globals.begin(), globals.end(), compareSymbol);

  //
  // collect functions and apply canonical sort
  //
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    if (fn->hasFlag(FLAG_GPU_CODEGEN) == gCodegenGPU){
      functions.push_back(fn);
    }
  }
  std::sort(functions.begin(), functions.end(), compareSymbol);

  codegen_header_compilation_config();

  if (fLibraryCompile) {
    codegen_library_header(functions);
    codegen_library_python(functions);
    codegen_library_fortran(functions);
  }

  FILE* hdrfile = info->cfile;

  if( hdrfile) {
    // Insert include guard to help MLI builds avoid multiple inclusion.
    fprintf(hdrfile, "#ifndef CHPL_GEN_HEADER_INCLUDE_GUARD\n");
    fprintf(hdrfile, "#define CHPL_GEN_HEADER_INCLUDE_GUARD\n");

    // This is done in runClang for LLVM version.
    fprintf(hdrfile, "\n#define CHPL_GEN_CODE\n\n");

    // Include sys_basic.h to get C types always defined,
    // proper library .h inclusion
    fprintf(hdrfile, "#include \"sys_basic.h\"\n");
    genIncludeCommandLineHeaders(hdrfile);

    fprintf(hdrfile, "#include \"stdchpl.h\"\n");

#ifdef HAVE_LLVM
    //include generated extern C header file
    if (fAllowExternC && gAllExternCode.filename != NULL) {
      fprintf(hdrfile, "%s", astr("#include \"", gAllExternCode.filename, "\"\n"));
      // If we wanted to, here is where we would re-enable
      // the memory warning macros.
    }
#endif
  }

  assignClassIds();
  genClassIDs(types, true);
  genSubclassArray(true);
  genClassNames(types, true);

  genComment("Class Prototypes");
  forv_Vec(TypeSymbol, typeSymbol, types) {
    if (!typeSymbol->hasFlag(FLAG_REF) && !typeSymbol->hasFlag(FLAG_DATA_CLASS))
    {
      typeSymbol->codegenPrototype();
    }
  }

  // codegen enumerated types
  genComment("Enumerated Types");
  forv_Vec(TypeSymbol, typeSymbol, types) {
    if (toEnumType(typeSymbol->type)) {
      typeSymbol->codegenDef();
    }
  }

  // codegen records/unions/references/data class in topological order
  genComment("Records, Unions, Data Class, References (Hierarchically)");
  forv_Vec(TypeSymbol, ts, types) {
    if (AggregateType* ct = toAggregateType(ts->type))
      codegen_aggregate_def(ct);
  }

  // codegen remaining types
  genComment("Classes");
  forv_Vec(TypeSymbol, typeSymbol, types) {
    if (isClass(typeSymbol->type) &&
        !typeSymbol->hasFlag(FLAG_REF) &&
        !typeSymbol->hasFlag(FLAG_DATA_CLASS) &&
        typeSymbol->hasFlag(FLAG_NO_OBJECT) &&
        !typeSymbol->hasFlag(FLAG_OBJECT_CLASS))
      typeSymbol->codegenDef();
  }

  //
  // codegen class definitions in breadth first order starting with
  // "object" and following its dispatch children
  //

  Vec<TypeSymbol*> next, current;

  current.add(dtObject->symbol);

  while (current.n) {
    forv_Vec(TypeSymbol, ts, current) {
      ts->codegenDef();

      if (AggregateType* at = toAggregateType(ts->type)) {
        forv_Vec(AggregateType, child, at->dispatchChildren) {
          if (child != NULL) {
            next.set_add(child->symbol);
          }
        }
      }
    }

    current.clear();
    current.move(next);
    current.set_to_vec();

    qsort(current.v, current.n, sizeof(current.v[0]), compareSymbol2);

    next.clear();
  }

  if(!info->cfile) {
    // Codegen any type annotations that are necessary.
    // Start with primitive types in case they are referenced by
    // records or classes.
    forv_Vec(TypeSymbol, typeSymbol, gTypeSymbols) {
      if (typeSymbol->defPoint->parentExpr == rootModule->block &&
          isPrimitiveType(typeSymbol->type) &&
          typeSymbol->llvmType) {
        typeSymbol->codegenMetadata();
      }
    }
    forv_Vec(TypeSymbol, typeSymbol, types) {
      if (!isDecoratedClassType(typeSymbol->type))
        typeSymbol->codegenMetadata();
    }
    // Aggregate annotations for class objects must wait until all other
    // type annotations are defined, because there might be cycles.
    forv_Vec(TypeSymbol, typeSymbol, types) {
      if (isClass(typeSymbol->type))
        typeSymbol->codegenAggMetadata();
    }
  }

  genComment("Function Prototypes");
  for_vector(FnSymbol, fnSymbol, functions) {
    fnSymbol->codegenPrototype();
  }

  genComment("Function Pointer Table");
  for_vector(FnSymbol, fn2, functions) {
    if (fn2->hasFlag(FLAG_BEGIN_BLOCK) ||
        fn2->hasFlag(FLAG_COBEGIN_OR_COFORALL_BLOCK) ||
        fn2->hasFlag(FLAG_ON_BLOCK)) {
    ftableVec.push_back(fn2);
    ftableMap[fn2] = ftableVec.size()-1;
    }
  }

  genFtable(ftableVec,true);
  genFinfo(ftableVec,true);

  genComment("Virtual Method Table");
  genVirtualMethodTable(types,true);

  genComment("Global Variables");
  forv_Vec(VarSymbol, varSymbol, globals) {
    varSymbol->codegenGlobalDef(true);
  }
  flushStatements();

  genGlobalInt("chpl_numGlobalsOnHeap", numGlobalsOnHeap, true);
  int globals_registry_static_size = (numGlobalsOnHeap ? numGlobalsOnHeap : 1);
  if( hdrfile ) {
    fprintf(hdrfile, "\nextern ptr_wide_ptr_t chpl_globals_registry[%d];\n",
                    globals_registry_static_size);
  } else {
#ifdef HAVE_LLVM
    llvm::Type* ptr_wide_ptr_t = info->lvt->getType("ptr_wide_ptr_t");
    INT_ASSERT(ptr_wide_ptr_t);

    if(llvm::GlobalVariable *GVar = llvm::cast_or_null<llvm::GlobalVariable>(
          info->module->getNamedGlobal("chpl_globals_registry"))) {
      GVar->eraseFromParent();
    }
    llvm::GlobalVariable *chpl_globals_registryGVar =
      llvm::cast<llvm::GlobalVariable>(
          info->module->getOrInsertGlobal("chpl_globals_registry",
            llvm::ArrayType::get(
              ptr_wide_ptr_t,
              globals_registry_static_size)));
    chpl_globals_registryGVar->setInitializer(
        llvm::Constant::getNullValue(
          chpl_globals_registryGVar->getType()->getContainedType(0)));
    info->lvt->addGlobalValue("chpl_globals_registry",
                              chpl_globals_registryGVar, GEN_PTR, true);
#endif
  }
  if( hdrfile ) {
      fprintf(hdrfile, "\nextern const char* chpl_mem_descs[];\n");
    } else {
#ifdef HAVE_LLVM
    std::vector<llvm::Constant *> memDescTable;
    forv_Vec(const char*, memDesc, memDescsVec) {
      memDescTable.push_back(llvm::cast<llvm::GlobalVariable>(
            new_CStringSymbol(memDesc)->codegen().val)->getInitializer());
    }
    llvm::ArrayType *memDescTableType = llvm::ArrayType::get(
        llvm::IntegerType::getInt8PtrTy(info->module->getContext()),
        memDescTable.size());

    if(llvm::GlobalVariable *GVar =llvm::cast_or_null<llvm::GlobalVariable>(
          info->module->getNamedGlobal("chpl_mem_descs"))) {
      GVar->eraseFromParent();
    }

    llvm::GlobalVariable *chpl_memDescsGVar = llvm::cast<llvm::GlobalVariable>(
        info->module->getOrInsertGlobal("chpl_mem_descs", memDescTableType));
    chpl_memDescsGVar->setInitializer(
        llvm::ConstantArray::get(memDescTableType, memDescTable));
    chpl_memDescsGVar->setConstant(true);
    info->lvt->addGlobalValue("chpl_mem_descs",chpl_memDescsGVar,GEN_PTR,true);
#endif
  }

  genGlobalInt("chpl_mem_numDescs", memDescsVec.size(), true);

  //
  // add table of private-broadcast constants
  //
  if( hdrfile ) {
    fprintf(hdrfile, "\nextern void* const chpl_private_broadcast_table[];\n");
  } else {
#ifdef HAVE_LLVM
    llvm::Type *private_broadcastTableEntryType =
      llvm::IntegerType::getInt8PtrTy(info->module->getContext());

    std::vector<llvm::Constant *> private_broadcastTable;

    int broadcastID = 0;
    forv_Vec(CallExpr, call, gCallExprs) {
      if (call->isPrimitive(PRIM_PRIVATE_BROADCAST)) {
        SymExpr* se = toSymExpr(call->get(1));
        INT_ASSERT(se);

        private_broadcastTable.push_back(llvm::cast<llvm::Constant>(
              info->irBuilder->CreatePointerCast(
                info->lvt->getValue(se->symbol()->cname).val,
                private_broadcastTableEntryType)));
        // To preserve operand order, this should be insertAtTail.
        call->insertAtHead(new_IntSymbol(broadcastID++));
      }
    }

    if(llvm::GlobalVariable *GVar = llvm::cast_or_null<llvm::GlobalVariable>(
          info->module->getNamedGlobal("chpl_private_broadcast_table"))) {
      GVar->eraseFromParent();
    }

    llvm::ArrayType *private_broadcastTableType =
      llvm::ArrayType::get(private_broadcastTableEntryType,
                          private_broadcastTable.size());
    llvm::GlobalVariable *private_broadcastTableGVar =
      llvm::cast<llvm::GlobalVariable>(
          info->module->getOrInsertGlobal("chpl_private_broadcast_table",
                                          private_broadcastTableType));
    private_broadcastTableGVar->setInitializer(
        llvm::ConstantArray::get(
          private_broadcastTableType, private_broadcastTable));
    info->lvt->addGlobalValue("chpl_private_broadcast_table",
                              private_broadcastTableGVar, GEN_PTR, true);
    genGlobalInt("chpl_private_broadcast_table_len",
                 private_broadcastTable.size(), false);
#endif
  }

  if (hdrfile) {
    fprintf(hdrfile, "#include \"chpl-gen-includes.h\"\n");
  }
}

// Sometimes we have to define a type while code generating.
// When that happens, we need to add a little bit to the header...
// This is only needed for C (since in LLVM we must add
//  the types as we use them).
static void codegen_header_addons() {
  forv_Vec(TypeSymbol, ts, gTypeSymbols) {
    if (ts->defPoint->parentExpr != rootModule->block) {
      if (AggregateType* ct = toAggregateType(ts->type))
        codegen_aggregate_def(ct);
    }
  }
}

static void
codegen_config() {
  GenInfo* info = gGenInfo;

  // LLVM backend need _config.c generated for the launcher,
  // so we produce the C for it either way.
  if (gCodegenGPU == false) {
    FILE* mainfile = info->cfile;
    if( mainfile ) fprintf(mainfile, "#include \"_config.c\"\n");
    fileinfo configFile;
    openCFile(&configFile, "_config.c");
    FILE* outfile = configFile.fptr;
    info->cfile = outfile;

    fprintf(outfile, "#include \"error.h\"\n\n");

    genGlobalInt("mainHasArgs", mainHasArgs, false);
    genGlobalInt("mainPreserveDelimiter", mainPreserveDelimiter, false);

    fprintf(outfile, "void CreateConfigVarTable(void) {\n");
    fprintf(outfile, "initConfigVarTable();\n");

    forv_Vec(VarSymbol, var, gVarSymbols) {
      if (var->hasFlag(FLAG_CONFIG) && !var->isType()) {
        fprintf(outfile, "installConfigVar(\"%s\", \"", var->name);
        Type* type = var->type;
        if (type->symbol->hasFlag(FLAG_WIDE_CLASS))
          type = type->getField("addr")->type;
        if (type->symbol->hasFlag(FLAG_HEAP))
          type = type->getField("value")->type;
        if (type->symbol->hasFlag(FLAG_WIDE_CLASS))
          type = type->getField("addr")->type;
        fprintf(outfile, "%s", type->symbol->name);
        if (var->getModule()->modTag == MOD_INTERNAL) {
          fprintf(outfile, "\", \"Built-in\"");
        } else {
          fprintf(outfile, "\", \"%s\"", var->getModule()->name);
        }
        fprintf(outfile,", /* private = */ %d", var->hasFlag(FLAG_PRIVATE));
        fprintf(outfile,", /* deprecated = */ %d",
                var->hasFlag(FLAG_DEPRECATED));
        fprintf(outfile,", \"%s\");\n", var->getDeprecationMsg());

      }
    }

    fprintf(outfile, "}\n\n\n");

    closeCFile(&configFile);
    info->cfile = mainfile;
  }


  if( fLlvmCodegen ) {
#ifdef HAVE_LLVM
    llvm::FunctionType *createConfigType;
    llvm::Function *createConfigFunc;
    genGlobalInt("mainHasArgs", mainHasArgs, false);
    genGlobalInt("mainPreserveDelimiter", mainPreserveDelimiter, false);
    if((createConfigFunc = getFunctionLLVM("CreateConfigVarTable"))) {
      createConfigType = createConfigFunc->getFunctionType();
    }
    else {
      createConfigType = llvm::FunctionType::get(
          llvm::Type::getVoidTy(info->module->getContext()), false);
      createConfigFunc =
        llvm::Function::Create(createConfigType,
                               llvm::Function::ExternalLinkage,
                               "CreateConfigVarTable", info->module);
    }

    llvm::BasicBlock *createConfigBlock =
      llvm::BasicBlock::Create(info->module->getContext(),
                               "entry", createConfigFunc);
    info->irBuilder->SetInsertPoint(createConfigBlock);

    llvm::Function *initConfigFunc = getFunctionLLVM("initConfigVarTable");
    info->irBuilder->CreateCall(initConfigFunc, {} );

    llvm::Function *installConfigFunc = getFunctionLLVM("installConfigVar");

    forv_expanding_Vec(VarSymbol, var, gVarSymbols) {
      if (var->hasFlag(FLAG_CONFIG) && !var->isType()) {
        std::vector<llvm::Value *> args (6);
        {
          GenRet gen = new_CStringSymbol(var->name)->codegen();
#if HAVE_LLVM_VER >= 130
          args[0] = info->irBuilder->CreateLoad(gen.val->getType(), gen.val);
#else
          args[0] = info->irBuilder->CreateLoad(gen.val);
#endif
        }

        Type* type = var->type;
        if (type->symbol->hasFlag(FLAG_WIDE_CLASS)) {
          type = type->getField("addr")->type;
        }
        if (type->symbol->hasFlag(FLAG_HEAP)) {
          type = type->getField("value")->type;
        }
        if (type->symbol->hasFlag(FLAG_WIDE_CLASS)) {
          type = type->getField("addr")->type;
        }
        {
          GenRet gen = new_CStringSymbol(type->symbol->name)->codegen();
#if HAVE_LLVM_VER >= 130
          args[1] = info->irBuilder->CreateLoad(gen.val->getType(), gen.val);
#else
          args[1] = info->irBuilder->CreateLoad(gen.val);
#endif
        }

        if (var->getModule()->modTag == MOD_INTERNAL) {
          GenRet gen = new_CStringSymbol("Built-in")->codegen();
#if HAVE_LLVM_VER >= 130
          args[2] = info->irBuilder->CreateLoad(gen.val->getType(), gen.val);
#else
          args[2] = info->irBuilder->CreateLoad(gen.val);
#endif
        }
        else {
          GenRet gen = new_CStringSymbol(var->getModule()->name)->codegen();
#if HAVE_LLVM_VER >= 130
          args[2] =info->irBuilder->CreateLoad(gen.val->getType(), gen.val);
#else
          args[2] =info->irBuilder->CreateLoad(gen.val);
#endif
        }

        args[3] = info->irBuilder->getInt32(var->hasFlag(FLAG_PRIVATE));

        args[4] = info->irBuilder->getInt32(var->hasFlag(FLAG_DEPRECATED));
        {
          GenRet gen = new_CStringSymbol(var->getDeprecationMsg())->codegen();
#if HAVE_LLVM_VER >= 130
          args[5] = info->irBuilder->CreateLoad(gen.val->getType(), gen.val);
#else
          args[5] = info->irBuilder->CreateLoad(gen.val);
#endif
        }
        info->irBuilder->CreateCall(installConfigFunc, args);
      }
    }
    info->irBuilder->CreateRetVoid();
    //llvm::verifyFunction(*createConfigFunc);
#endif
  }
}

static const char* generateFileName(ChainHashMap<char*, StringHashFns, int>& filenames, const char* name, const char* currentModuleName){
  // Macs are case-insensitive when it comes to files, so
  // the following bit of code creates a unique filename
  // with case-insensitivity taken into account

  // create the lowercase filename
  char lowerFilename[FILENAME_MAX];
  sprintf(lowerFilename, "%s", currentModuleName);
  for (unsigned int i=0; i<strlen(lowerFilename); i++) {
    lowerFilename[i] = tolower(lowerFilename[i]);
  }

  // create a filename by bumping a version number until we get a
  // filename we haven't seen before
  char filename[FILENAME_MAX];
  sprintf(filename, "%s", lowerFilename);
  int version = 1;
  while (filenames.get(filename)) {
    version++;
    int wanted_to_write = snprintf(filename, sizeof(filename), "%s%d",
                                   lowerFilename, version);
    if (wanted_to_write < 0) {
      USR_FATAL("character encoding error while generating file name");
    } else if ((size_t)wanted_to_write >= sizeof(filename)) {
      USR_FATAL("module name '%s' is too long to be the basis for a file name",
                currentModuleName);
    }
  }
  filenames.put(filename, 1);

  // build the real filename using that version number -- preserves
  // case by default by going back to currentModule->name rather
  // than using the lowercase filename
  if (version == 1) {
    sprintf(filename, "%s", currentModuleName);
  } else {
    sprintf(filename, "%s%d", currentModuleName, version);
  }

  name = astr(filename);
  return name;
}


static bool
shouldChangeArgumentTypeToRef(ArgSymbol* arg) {
  FnSymbol* fn = toFnSymbol(arg->defPoint->parentSymbol);

  bool shouldPassRef = (arg->intent & INTENT_FLAG_REF) ||
                       arg->requiresCPtr();

  bool alreadyRef = arg->typeInfo()->symbol->hasFlag(FLAG_REF) ||
                    arg->isRef() ||
                    arg->isWideRef();

  // Only change argument types for functions with a ref intent
  // that don't already have an argument being passed by ref
  // and that aren't extern functions.
  return (shouldPassRef &&
          !alreadyRef &&
          !fn->hasFlag(FLAG_EXTERN) &&
          // TODO: Consider flag for export wrappers instead of this.
          !(fLibraryCompile && fn->hasFlag(FLAG_EXPORT)) &&
          !arg->hasFlag(FLAG_NO_CODEGEN));
}

static void
adjustArgSymbolTypesForIntent(void)
{
  // Adjust ArgSymbols that have ref/const ref concrete
  // intent so that their type is ref. This allows the
  // rest of this code to work as expected.
  forv_Vec(ArgSymbol, arg, gArgSymbols) {
    if (shouldChangeArgumentTypeToRef(arg)) {
      arg->qual   = QUAL_REF;
      arg->intent = INTENT_REF;
    }
  }
}


static void convertSymbolToRefType(Symbol* sym) {
  QualifiedType q = sym->qualType();
  Type* type      = q.type();
  if (q.isRef() && !q.isRefType()) {
    type = getOrMakeRefTypeDuringCodegen(type);
  } else if (q.isWideRef() && !q.isWideRefType()) {
    type = getOrMakeRefTypeDuringCodegen(type);
    type = getOrMakeWideTypeDuringCodegen(type);
  }
  sym->type = type;
  if (type->symbol->hasFlag(FLAG_REF)) {
    sym->qual = QUAL_REF;
  } else if (type->symbol->hasFlag(FLAG_WIDE_REF)) {
    sym->qual = QUAL_WIDE_REF;
  }
}

static void convertToRefTypes() {
  forv_expanding_Vec(VarSymbol, sym, gVarSymbols) convertSymbolToRefType(sym);
  forv_Vec(ArgSymbol, sym, gArgSymbols) convertSymbolToRefType(sym);
  forv_Vec(ShadowVarSymbol, sym, gShadowVarSymbols) convertSymbolToRefType(sym);

  forv_Vec(FnSymbol, fn, gFnSymbols) {
    if (fn->getReturnSymbol()) {
      fn->retType = fn->getReturnSymbol()->type;
    }
  }

#undef updateSymbols
}

extern bool printCppLineno;
debug_data *debug_info=NULL;




#ifdef HAVE_LLVM

// handle e.g. chpl_clang_builtin_wrapper_cabs

static bool hasWrapper(const char *name)
{
  auto it = chplClangBuiltinWrappedFunctions.find(name);
  if(it != end(chplClangBuiltinWrappedFunctions))
    return true;
  return false;
}

static const char* getClangBuiltinWrappedName(const char* name)
{
  auto it = chplClangBuiltinWrappedFunctions.find(name);
  if(it != end(chplClangBuiltinWrappedFunctions))
    return astr(WRAPPER_PREFIX, name);
  return name;
}
#endif

// Set the executable name to the name of the file containing the
// main module (minus its path and extension) if it isn't set
// already.  If in library mode, set the name of the header file as well.
static void setupDefaultFilenames() {
  if (executableFilename[0] == '\0') {
    ModuleSymbol* mainMod = ModuleSymbol::mainModule();
    const char* mainModFilename = mainMod->astloc.filename();
    const char* filename = stripdirectories(mainModFilename);

    // "Executable" name should be given a "lib" prefix in library compilation,
    // and just the main module name in normal compilation.
    if (fLibraryCompile) {
      // If the header name isn't set either, don't use the prefix version
      if (libmodeHeadername[0] == '\0') {
        // copy from that slash onwards into the libmodeHeadername,
        // saving space for a `\0` terminator
        if (strlen(filename) >= sizeof(libmodeHeadername)) {
          INT_FATAL("input filename exceeds header filename buffer size");
        }
        strncpy(libmodeHeadername, filename, sizeof(libmodeHeadername)-1);
        libmodeHeadername[sizeof(libmodeHeadername)-1] = '\0';
        // remove the filename extension from the library header name.
        char* lastDot = strrchr(libmodeHeadername, '.');
        if (lastDot == NULL) {
          INT_FATAL(mainMod,
                    "main module filename is missing its extension: %s\n",
                    libmodeHeadername);
        }
        *lastDot = '\0';
      }
      if (strlen(filename) >= sizeof(executableFilename) - 3) {
        INT_FATAL("input filename exceeds executable filename buffer size");
      }
      strncpy(executableFilename, filename,
              sizeof(executableFilename)-1);

      if (fLibraryPython && pythonModulename[0] == '\0') {
        strncpy(pythonModulename, filename, sizeof(pythonModulename)-1);
        pythonModulename[sizeof(pythonModulename)-1] = '\0';
        char* lastDot = strrchr(pythonModulename, '.');
        if (lastDot == NULL) {
          INT_FATAL(mainMod,
                    "main module filename is missing its extension: %s\n",
                    pythonModulename);
        }
        *lastDot = '\0';
      }

    } else {
      // copy from that slash onwards into the executableFilename,
      // saving space for a `\0` terminator
      if (strlen(filename) >= sizeof(executableFilename)) {
        INT_FATAL("input filename exceeds executable filename buffer size");
      }
      strncpy(executableFilename, filename, sizeof(executableFilename)-1);
      executableFilename[sizeof(executableFilename)-1] = '\0';
    }

    // remove the filename extension from the executable filename
    char* lastDot = strrchr(executableFilename, '.');
    if (lastDot == NULL) {
      INT_FATAL(mainMod, "main module filename is missing its extension: %s\n",
                executableFilename);
    }
    *lastDot = '\0';

  }

  // If we're in library mode and the executable name was set but the header
  // name wasn't, use the executable name for the header name as well
  if (fLibraryCompile && libmodeHeadername[0] == '\0') {
    strncpy(libmodeHeadername, executableFilename, sizeof(libmodeHeadername)-1);
    libmodeHeadername[sizeof(libmodeHeadername)-1] = '\0';
  }

  // If we're in library mode and the library name was explicitly set, use that
  // name for the python module.
  if (fLibraryCompile && fLibraryPython && pythonModulename[0] == '\0') {
    strncpy(pythonModulename, executableFilename, sizeof(pythonModulename)-1);
    pythonModulename[sizeof(pythonModulename)-1] = '\0';
  }
}

static std::map<const char*, Type*> cnameToTypeMap;

void gatherTypesForCodegen(void) {
  // A reasonable alternative to this code might be to
  // map types like c_int to Clang types and query Clang for their sizes.
  // See for example addMinMax in clangUtil.cpp.

  // Gather type cnames for use in code generation
  // must be run before clang parses macros
  forv_Vec(VarSymbol, var, gVarSymbols) {
    if (var->hasFlag(FLAG_EXTERN) && var->hasFlag(FLAG_TYPE_VARIABLE)) {
      Type* t = NULL;
      if (var->type != dtUnknown) {
        t = var->type;
      } else {
        // handle extern type c_int = int(32) e.g. before normalize
        DefExpr* def = var->defPoint;
        if (CallExpr* call = toCallExpr(def->init)) {
          t = typeForTypeSpecifier(call, false);
        }
      }

      if (t != NULL)
        cnameToTypeMap[var->cname] = t;
    }
  }

  forv_Vec(TypeSymbol, ts, gTypeSymbols) {
    if (ts->type != dtUnknown)
      cnameToTypeMap[ts->cname] = ts->type;
  }
}

Type* getNamedTypeDuringCodegen(const char* name) {
  std::map<const char*, Type*>::iterator it;

  name = astr(name);

  it = cnameToTypeMap.find(name);
  if (it != cnameToTypeMap.end()) {
    return it->second;
  }

  return NULL;
}


// Do this once for CPU and GPU
static void codegenPartOne() {
  if( fLLVMWideOpt ) {
    // --llvm-wide-opt is picky about other settings.
    // Check them here.
    if (!fLlvmCodegen )
      USR_FATAL("--llvm-wide-opt requires CHPL_TARGET_COMPILER=llvm");
  }

  // Prepare primitives for codegen
  CallExpr::registerPrimitivesForCodegen();

  gatherTypesForCodegen();
  setupDefaultFilenames();

  SET_LINENO(rootModule);

  adjustArgSymbolTypesForIntent();

  convertToRefTypes();

  // Wrap calls to chosen functions from c library
  if (fLlvmCodegen) {
#ifdef HAVE_LLVM
    forv_Vec(FnSymbol, fn, gFnSymbols) {
      if (fn->hasFlag(FLAG_EXTERN)) {
        if(hasWrapper(fn->cname))
          fn->cname = getClangBuiltinWrappedName(fn->cname);
      }
    }
#endif
  }

  // Vectors to store different symbol names to be used while uniquifying
  std::set<const char*> cnames;
  std::vector<TypeSymbol*> types;
  std::vector<FnSymbol*> functions;
  std::vector<VarSymbol*> globals;

  uniquify_names(cnames, types, functions, globals);
}

static fileinfo hdrfile    = { NULL, NULL, NULL };
static fileinfo mainfile   = { NULL, NULL, NULL };
static fileinfo defnfile   = { NULL, NULL, NULL };
static fileinfo strconfig  = { NULL, NULL, NULL };
static fileinfo modulefile = { NULL, NULL, NULL };


#ifdef HAVE_LLVM
static void embedGpuCode() {
  // Codegen forks a process to generate a .fatbin file that packages assembled GPU kernel code.
  // This function (embedGpuCode) is called by the main process after the forked thread rejoins and
  // reads this .fatbin file and dumps its contents into a global variable in the generated code.
  // The compiled chapel program then calls into the runtime library, which reads this variable,
  // sends the code off to the GPU, and launches kernels as needed.
  SET_LINENO(rootModule);
  std::string fatbinFilename = genIntermediateFilename("chpl__gpu.fatbin");
  std::string buffer;
  chpl::ErrorMessage err;
  chpl::readfile(fatbinFilename.c_str(), buffer, err);
  if(!err.isEmpty()) {
    USR_FATAL("%s", err.message().c_str());
  }

  genGlobalRawString("chpl_gpuBinary", buffer, buffer.length());
}
#endif

// Do this for GPU and then do for CPU
static void codegenPartTwo() {
  // Initialize the global gGenInfo for C code generation.
  gGenInfo = new GenInfo();

  if (fMultiLocaleInterop) {
    codegenMultiLocaleInteropWrappers();
  }

  if (fLlvmCodegen) {
#ifndef HAVE_LLVM
    USR_FATAL("This compiler was built without LLVM support");
#else
    INT_ASSERT(gGenInfo != NULL);
    runClang(NULL);
#endif
  }

  SET_LINENO(rootModule);

  GenInfo* info     = gGenInfo;

  INT_ASSERT(info);

  // Populate functionCNameAstrToSymbol map
  for_alive_in_Vec(FnSymbol, fn, gFnSymbols) {
    if (fn->hasFlag(FLAG_NO_CODEGEN)) {
      // ignore it
    } else {
      const char* cname = astr(fn->cname);
      bool skip = false;
      if (fLlvmCodegen) {
#ifdef HAVE_LLVM
        // Check for a decl from clang; in that case don't add it
        // to the map. (This avoids problems with 'printf' having
        // multiple versions with different numbers of arguments).
        if (getFunctionDeclClang(cname) != nullptr) {
          skip = true;
        }
#endif
      }
      if (skip == false) {
        info->functionCNameAstrToSymbol.insert(std::make_pair(cname, fn));
      }
    }
  }

  if( fLlvmCodegen ) {
#ifdef HAVE_LLVM

    if(fIncrementalCompilation)
      USR_FATAL("Incremental compilation is not yet supported with LLVM");

    if(debugCCode)
    {
      debug_info = new debug_data(*info->module);
    }
    if(debug_info) {
      // first find the main module, this will be the compile unit.
      forv_Vec(ModuleSymbol, currentModule, allModules) {
        if(currentModule->hasFlag(FLAG_MODULE_FROM_COMMAND_LINE_FILE)) {
          //So, this is pretty quick. I'm assuming that the main module is in the current dir, no optimization (need to figure out how to get this)
          // and no compile flags, since I can't figure out how to get that either.
          const char *current_dir = "./";
          const char *empty_string = "";
          debug_info->create_compile_unit(currentModule->astloc.filename(), current_dir, false, empty_string);
          break;
        }
      }
    }

    // When doing codegen for programs that have GPU kernels we fork the
    // processes. In one process gCodegenGPU is true and we generate a .fatbin file,
    // in the other gCodegenGpu is false and we'll consume the fatbin file
    // and embed its contents into the generated code.
    if (localeUsesGPU() && !gCodegenGPU) {
      embedGpuCode();
    }

    prepareCodegenLLVM();
#endif
  } else {
    openCFile(&hdrfile,  "chpl__header", "h");
    openCFile(&mainfile, "_main",        "c");
    openCFile(&defnfile, "chpl__defn",    "c");
    openCFile(&strconfig,  "chpl_str_config", "c");

    zlineToFileIfNeeded(rootModule, mainfile.fptr);
    fprintf(mainfile.fptr, "#include \"chpl_str_config.c\"\n");
    fprintf(mainfile.fptr, "#include \"chpl__header.h\"\n");
    fprintf(mainfile.fptr, "#include \"%s.c\"\n", sCfgFname);
    fprintf(mainfile.fptr, "#include \"chpl__defn.c\"\n");

    std::vector<const char*> userFileName;
    if(fIncrementalCompilation) {
      ChainHashMap<char*, StringHashFns, int> fileNameHashMap;
      forv_Vec(ModuleSymbol, currentModule, allModules) {
        const char* filename = NULL;
        filename = generateFileName(fileNameHashMap, filename, currentModule->name);
        if(currentModule->modTag == MOD_USER) {
          openCFile(&modulefile, filename, "c");
          int modulePathLen = strlen(astr(modulefile.pathname));
          char path[FILENAME_MAX];
          strncpy(path, astr(modulefile.pathname), modulePathLen-2);
          path[modulePathLen-2]='\0';
          userFileName.push_back(astr(path));
          closeCFile(&modulefile);
        }
      }
    }
    codegen_makefile(&mainfile, NULL, NULL, false, userFileName);
  }

  if (fLibraryCompile && fLibraryMakefile) {
    codegen_library_makefile();
  }

  // Vectors to store different symbol names to be used while generating header
  std::set<const char*> cnames;
  std::vector<TypeSymbol*> types;
  std::vector<FnSymbol*> functions;
  std::vector<VarSymbol*> globals;

  // This dumps the generated sources into the build directory.
  info->cfile = hdrfile.fptr;
  codegen_header(cnames, types, functions, globals);

  // Prepare the LLVM IR dumper for code generation
  // This needs to happen after protectNameFromC which happens
  // currently in codegen_header.
  preparePrintLlvmIrForCodegen();

  info->cfile = defnfile.fptr;
  codegen_defn(cnames, types, functions, globals);
  info->cfile = mainfile.fptr;
  if ( gCodegenGPU == false ) {
    codegen_config();
  }

  // Don't need to do most of the rest of the function for LLVM;
  // just codegen the modules.
  if( fLlvmCodegen ) {
#ifdef HAVE_LLVM
    checkAdjustedDataLayout();
    forv_Vec(ModuleSymbol, currentModule, allModules) {
      currentModule->codegenDef();
    }

    finishCodegenLLVM();
#endif
  } else {
    ChainHashMap<char*, StringHashFns, int> fileNameHashMap;
    forv_Vec(ModuleSymbol, currentModule, allModules) {
      const char* filename = NULL;
      filename = generateFileName(fileNameHashMap, filename,currentModule->name);

      openCFile(&modulefile, filename, "c");
      info->cfile = modulefile.fptr;
      if(fIncrementalCompilation && (currentModule->modTag == MOD_USER))
        fprintf(modulefile.fptr, "#include \"chpl__header.h\"\n");
      currentModule->codegenDef();

      closeCFile(&modulefile);

      if(!(fIncrementalCompilation && (currentModule->modTag == MOD_USER)))
        fprintf(mainfile.fptr, "#include \"%s%s\"\n", filename, ".c");
    }

    if (fMultiLocaleInterop) {
      codegenMultiLocaleInteropWrappers();
    }

    fprintf(strconfig.fptr, "#include \"chpl-string.h\"\n");
    fprintf(strconfig.fptr, "chpl_string defaultStringValue=\"\";\n");

    info->cfile = hdrfile.fptr;
    codegen_header_addons();

    fprintf(hdrfile.fptr, "\n#endif");
    fprintf(hdrfile.fptr, " /* END CHPL_GEN_HEADER_INCLUDE_GUARD */\n");

    closeCFile(&hdrfile);
    fprintf(mainfile.fptr, "/* last line not #include to avoid gcc bug */\n");
    closeCFile(&mainfile);
    closeCFile(&defnfile);
    closeCFile(&strconfig);
  }

  if (fPrintEmittedCodeSize)
  {
    fprintf(stderr, "Statements emitted: %d\n", gStmtCount);
  }


}

void codegen() {
  if (no_codegen)
    return;

  codegenPartOne();

  if (localeUsesGPU()) {
    // We use the temp dir to output a fatbin file and read it between the forked and main proccess.
    // We need to generate the name for the temp directory before we do the fork (since this
    // name uses the PID).
    ensureTmpDirExists();

    pid_t pid = fork();

    if (pid == 0) {
      // child process
      gCodegenGPU = true;
      codegenPartTwo();
      makeBinary();
      clean_exit(0);
    } else {
      // parent process
      INT_ASSERT(!gCodegenGPU);
      int status = 0;
      while (wait(&status) != pid) {
        // wait for child process
      }
      // If there was an error in GPU code generation then the .fatbin file (containing
      // the generated GPU code) was not created and we won't be able to continue.
      if(status != 0) {
        clean_exit(status);
      }
    }
  }

  codegenPartTwo();
}

void makeBinary(void) {
  if (no_codegen)
    return;

  if(fLlvmCodegen) {
#ifdef HAVE_LLVM
   makeBinaryLLVM();
#endif
  } else {
    const char* makeflags = printSystemCommands ? "-f " : "-s -f ";
    const char* command = astr(astr(CHPL_MAKE, " "),
                               makeflags,
                               getIntermediateDirName(), "/Makefile");
    mysystem(command, "compiling generated source");
  }

  if (gCodegenGPU == false) {
    if (fLibraryCompile && fLibraryPython) {
      codegen_make_python_module();
    }
  }
}

GenInfo::GenInfo()
         :   cfile(nullptr), cLocalDecls(), cStatements(),
             lineno(-1), filename(nullptr),
             functionCNameAstrToSymbol()
#ifdef HAVE_LLVM
             ,
             lvt(nullptr), module(nullptr), irBuilder(nullptr), mdBuilder(nullptr),
             loopStack(), currentStackVariables(),
             currentFunctionABI(nullptr),
             llvmContext(),
             tbaaRootNode(nullptr),
             tbaaUnionsNode(nullptr),
             noAliasDomain(nullptr),
             noAliasScopes(),
             noAliasScopeLists(),
             noAliasLists(),
             globalToWideInfo(),
             FPM_postgen(nullptr),
             clangInfo(nullptr)
#endif
{
}

std::string numToString(int64_t num)
{
  return int64_to_string(num);
}
std::string int64_to_string(int64_t i)
{
  char buf[32];
  snprintf(buf, sizeof(buf), "%" PRId64, i);
  std::string ret(buf);
  return ret;
}
std::string uint64_to_string(uint64_t i)
{
  char buf[32];
  snprintf(buf, sizeof(buf), "%" PRIu64, i);
  std::string ret(buf);
  return ret;
}
std::string real_to_string(double num)
{
  char buf[32];

  if (std::isfinite(num)) {
    if (std::signbit(num)) snprintf(buf, sizeof(buf), "-%a" , -num);
    else                   snprintf(buf, sizeof(buf), "%a" , num);
  } else if (std::isinf(num)) {
    if (std::signbit(num)) strncpy(buf, "-INFINITY", sizeof(buf));
    else                   strncpy(buf, "INFINITY", sizeof(buf));
  } else {
    if (std::signbit(num)) strncpy(buf, "-NAN", sizeof(buf));
    else                   strncpy(buf, "NAN", sizeof(buf));
  }
  std::string ret(buf);
  return ret;
}
void genComment(const char* comment, bool push) {
  GenInfo* info = gGenInfo;
  if( info->cfile ) {
    if (push) {
      std::string str = comment;
      info->cStatements.push_back("/*** "+str+" ***/ ");
    } else {
      fprintf(info->cfile, "/*** %s ***/\n\n", comment);
    }
  }
}

void flushStatements(void)
{
  GenInfo* info = gGenInfo;
  size_t i;
  if( info->cfile ) {
    for(i = 0; i < info->cLocalDecls.size(); ++i) {
      fprintf(info->cfile, "%s;\n", info->cLocalDecls[i].c_str());
    }
    for(i = 0; i < info->cStatements.size(); ++i) {
      fprintf(info->cfile, "%s", info->cStatements[i].c_str());
    }
    info->cLocalDecls.clear();
    info->cStatements.clear();
  }
}

void nprint_view(GenRet& gen) {
#ifdef HAVE_LLVM
  GenInfo* info = gGenInfo;
  llvm::Module* M = info->module;
#endif

  printf("GenRet {\n");
  if (!gen.c.empty())
    printf("c=%s\n", gen.c.c_str());
#ifdef HAVE_LLVM
  if (gen.val) {
    printf("val= ");
    fflush(stdout);
    gen.val->print(llvm::outs(), true);
    llvm::outs().flush();
    printf("\n");
  }
  if (gen.type) {
    printf("type= ");
    fflush(stdout);
    gen.type->print(llvm::outs(), true);
    llvm::outs().flush();
    printf("\n");
  }
  if (gen.surroundingStruct) {
    TypeSymbol* ts = gen.surroundingStruct->symbol;
    printf("surroundingStruct=%s (%i)\n", ts->name, ts->id);
  }
  if (gen.fieldOffset) {
    printf("fieldOffset=%i\n", (int) gen.fieldOffset);
  }
  if (gen.fieldTbaaTypeDescriptor) {
    printf("fieldTbaaTypeDescriptor= ");
    fflush(stdout);
    gen.fieldTbaaTypeDescriptor->print(llvm::outs(), M, true);
    llvm::outs().flush();
    printf("\n");
  }
  if (gen.aliasScope) {
    printf("aliasScope= ");
    fflush(stdout);
    gen.aliasScope->print(llvm::outs(), M, true);
    llvm::outs().flush();
    printf("\n");
  }
  if (gen.noalias) {
    printf("noalias= ");
    fflush(stdout);
    gen.noalias->print(llvm::outs(), M, true);
    llvm::outs().flush();
    printf("\n");
  }
#endif
  printf("canBeMarkedAsConstAfterStore=%i\n",
         (int) gen.canBeMarkedAsConstAfterStore);
  printf("alreadyStored %i\n", (int) gen.alreadyStored);
  if (gen.chplType) {
    TypeSymbol* ts = gen.chplType->symbol;
    printf("chplType=%s (%i)\n", ts->name, ts->id);
  }
  if (gen.isLVPtr == GEN_VAL) {
    printf("isLVPtr=GEN_VAL\n");
  }
  if (gen.isLVPtr == GEN_PTR) {
    printf("isLVPtr=GEN_PTR\n");
  }
  if (gen.isLVPtr == GEN_WIDE_PTR) {
    printf("isLVPtr=GEN_WIDE_PTR\n");
  }
  printf("isUnsigned %i\n", (int) gen.isUnsigned);
  printf("}\n");
}

void closeCodegenFiles() {
  // close the C files without trying to beautify
  closeCFile(&hdrfile, false);
  closeCFile(&mainfile, false);
  closeCFile(&defnfile, false);
  closeCFile(&strconfig, false);
  closeCFile(&modulefile, false);
}
