void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) { IF_LOG Logger::println("TypeInfoDeclaration_codegen(%s)", decl->toPrettyChars()); LOG_SCOPE; if (decl->ir->isDefined()) { return; } decl->ir->setDefined(); OutBuffer mangleBuf; mangleToBuffer(decl, &mangleBuf); const char *mangled = mangleBuf.peekString(); IF_LOG { Logger::println("type = '%s'", decl->tinfo->toChars()); Logger::println("typeinfo mangle: %s", mangled); } // Only declare the symbol if it isn't yet, otherwise the subtype of built-in // TypeInfos (rt.typeinfo.*) may clash with the base type when compiling the // rt.typeinfo.* modules. const auto irMangle = getIRMangledVarName(mangled, LINKd); llvm::GlobalVariable *gvar = gIR->module.getGlobalVariable(irMangle); if (!gvar) { LLType *type = DtoType(decl->type)->getPointerElementType(); // We need to keep the symbol mutable as the type is not declared as // immutable on the D side, and e.g. synchronized() can be used on the // implicit monitor. gvar = declareGlobal(decl->loc, gIR->module, type, irMangle, false); } IrGlobal *irg = getIrGlobal(decl, true); irg->value = gvar; emitTypeMetadata(decl); // check if the definition can be elided if (!global.params.useTypeInfo || !Type::dtypeinfo || isSpeculativeType(decl->tinfo) || builtinTypeInfo(decl->tinfo)) { return; } // define the TypeInfo global LLVMDefineVisitor v(gvar); decl->accept(&v); setLinkage({TYPEINFO_LINKAGE_TYPE, supportsCOMDAT()}, gvar); }
void ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar, VarDeclaration *vd) { if (!mustEmitFullDebugInfo()) return; Logger::println("D to dwarf global_variable"); LOG_SCOPE; assert(vd->isDataseg() || (vd->storage_class & (STCconst | STCimmutable) && vd->_init)); OutBuffer mangleBuf; mangleToBuffer(vd, &mangleBuf); #if LDC_LLVM_VER >= 400 auto DIVar = DBuilder.createGlobalVariableExpression( #else DBuilder.createGlobalVariable( #endif #if LDC_LLVM_VER >= 306 GetCU(), // context #endif vd->toChars(), // name mangleBuf.peekString(), // linkage name CreateFile(vd), // file vd->loc.linnum, // line num CreateTypeDescription(vd->type, false), // type vd->protection.kind == PROTprivate, // is local to unit #if LDC_LLVM_VER >= 400 nullptr // relative location of field #else llVar // value #endif ); #if LDC_LLVM_VER >= 400 llVar->addDebugInfo(DIVar); #endif }
/** * Creates the data symbol used to initialize a TLS variable for Mach-O. * * Params: * vd = the variable declaration for the symbol * s = the back end symbol corresponding to vd * * Returns: the newly created symbol */ Symbol *createTLVDataSymbol(VarDeclaration *vd, Symbol *s) { assert(config.objfmt == OBJ_MACH && I64 && (s->ty() & mTYLINK) == mTYthread); // Compute identifier for tlv symbol OutBuffer buffer; buffer.writestring(s->Sident); buffer.write("$tlv$init", 9); const char *tlvInitName = buffer.peekString(); // Compute type for tlv symbol type *t = type_fake(vd->type->ty); type_setty(&t, t->Tty | mTYthreadData); type_setmangle(&t, mangle(vd)); Symbol *tlvInit = symbol_name(tlvInitName, SCstatic, t); tlvInit->Sdt = NULL; tlvInit->Salignment = type_alignsize(s->Stype); if (vd->linkage == LINKcpp) tlvInit->Sflags |= SFLpublic; return tlvInit; }
void obj_write_deferred(Library *library) { for (size_t i = 0; i < obj_symbols_towrite.dim; i++) { Dsymbol *s = obj_symbols_towrite[i]; Module *m = s->getModule(); char *mname; if (m) { mname = m->srcfile->toChars(); lastmname = mname; } else { //mname = s->ident->toChars(); mname = lastmname; assert(mname); } obj_start(mname); static int count; count++; // sequence for generating names /* Create a module that's a doppelganger of m, with just * enough to be able to create the moduleinfo. */ OutBuffer idbuf; idbuf.printf("%s.%d", m ? m->ident->toChars() : mname, count); char *idstr = idbuf.peekString(); if (!m) { // it doesn't make sense to make up a module if we don't know where to put the symbol // so output it into it's own object file without ModuleInfo objmod->initfile(idstr, NULL, mname); toObjFile(s, false); objmod->termfile(); } else { idbuf.data = NULL; Identifier *id = Identifier::create(idstr, TOKidentifier); Module *md = Module::create(mname, id, 0, 0); md->members = Dsymbols_create(); md->members->push(s); // its only 'member' is s md->doppelganger = 1; // identify this module as doppelganger md->md = m->md; md->aimports.push(m); // it only 'imports' m md->massert = m->massert; md->munittest = m->munittest; md->marray = m->marray; genObjFile(md, false); } /* Set object file name to be source name with sequence number, * as mangled symbol names get way too long. */ const char *fname = FileName::removeExt(mname); OutBuffer namebuf; unsigned hash = 0; for (char *p = s->toChars(); *p; p++) hash += *p; namebuf.printf("%s_%x_%x.%s", fname, count, hash, global.obj_ext); FileName::free((char *)fname); fname = namebuf.extractString(); //printf("writing '%s'\n", fname); File *objfile = File::create(fname); obj_end(library, objfile); } obj_symbols_towrite.dim = 0; }
int runLINK() { #if _WIN32 if (global.params.is64bit) { OutBuffer cmdbuf; cmdbuf.writestring("/NOLOGO "); for (size_t i = 0; i < global.params.objfiles->dim; i++) { if (i) cmdbuf.writeByte(' '); const char *p = (*global.params.objfiles)[i]; const char *basename = FileName::removeExt(FileName::name(p)); const char *ext = FileName::ext(p); if (ext && !strchr(basename, '.')) { // Write name sans extension (but not if a double extension) writeFilename(&cmdbuf, p, ext - p - 1); } else writeFilename(&cmdbuf, p); FileName::free(basename); } if (global.params.resfile) { cmdbuf.writeByte(' '); writeFilename(&cmdbuf, global.params.resfile); } cmdbuf.writeByte(' '); if (global.params.exefile) { cmdbuf.writestring("/OUT:"); writeFilename(&cmdbuf, global.params.exefile); } else { /* Generate exe file name from first obj name. * No need to add it to cmdbuf because the linker will default to it. */ const char *n = (*global.params.objfiles)[0]; n = FileName::name(n); global.params.exefile = (char *)FileName::forceExt(n, "exe"); } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); cmdbuf.writeByte(' '); if (global.params.mapfile) { cmdbuf.writestring("/MAP:"); writeFilename(&cmdbuf, global.params.mapfile); } else if (global.params.map) { const char *fn = FileName::forceExt(global.params.exefile, "map"); const char *path = FileName::path(global.params.exefile); const char *p; if (path[0] == '\0') p = FileName::combine(global.params.objdir, fn); else p = fn; cmdbuf.writestring("/MAP:"); writeFilename(&cmdbuf, p); } for (size_t i = 0; i < global.params.libfiles->dim; i++) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEFAULTLIB:"); writeFilename(&cmdbuf, (*global.params.libfiles)[i]); } if (global.params.deffile) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEF:"); writeFilename(&cmdbuf, global.params.deffile); } if (global.params.symdebug) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEBUG"); // in release mode we need to reactivate /OPT:REF after /DEBUG if (global.params.release) cmdbuf.writestring(" /OPT:REF"); } if (global.params.dll) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DLL"); } for (size_t i = 0; i < global.params.linkswitches->dim; i++) { cmdbuf.writeByte(' '); cmdbuf.writestring((*global.params.linkswitches)[i]); } /* Append the path to the VC lib files, and then the SDK lib files */ const char *vcinstalldir = getenv("VCINSTALLDIR"); if (vcinstalldir) { cmdbuf.writestring(" \"/LIBPATH:"); cmdbuf.writestring(vcinstalldir); cmdbuf.writestring("lib\\amd64\""); } const char *windowssdkdir = getenv("WindowsSdkDir"); if (windowssdkdir) { cmdbuf.writestring(" \"/LIBPATH:"); cmdbuf.writestring(windowssdkdir); cmdbuf.writestring("lib\\x64\""); } char *p = cmdbuf.peekString(); const char *lnkfilename = NULL; size_t plen = strlen(p); if (plen > 7000) { lnkfilename = FileName::forceExt(global.params.exefile, "lnk"); File flnk(lnkfilename); flnk.setbuffer(p, plen); flnk.ref = 1; if (flnk.write()) error(Loc(), "error writing file %s", lnkfilename); if (strlen(lnkfilename) < plen) sprintf(p, "@%s", lnkfilename); } const char *linkcmd = getenv("LINKCMD64"); if (!linkcmd) linkcmd = getenv("LINKCMD"); // backward compatible if (!linkcmd) { if (vcinstalldir) { OutBuffer linkcmdbuf; linkcmdbuf.writestring(vcinstalldir); linkcmdbuf.writestring("bin\\amd64\\link"); linkcmd = linkcmdbuf.extractString(); } else linkcmd = "link"; } int status = executecmd(linkcmd, p); if (lnkfilename) { remove(lnkfilename); FileName::free(lnkfilename); } return status; } else { OutBuffer cmdbuf; global.params.libfiles->push("user32"); global.params.libfiles->push("kernel32"); for (size_t i = 0; i < global.params.objfiles->dim; i++) { if (i) cmdbuf.writeByte('+'); const char *p = (*global.params.objfiles)[i]; const char *basename = FileName::removeExt(FileName::name(p)); const char *ext = FileName::ext(p); if (ext && !strchr(basename, '.')) { // Write name sans extension (but not if a double extension) writeFilename(&cmdbuf, p, ext - p - 1); } else writeFilename(&cmdbuf, p); FileName::free(basename); } cmdbuf.writeByte(','); if (global.params.exefile) writeFilename(&cmdbuf, global.params.exefile); else { /* Generate exe file name from first obj name. * No need to add it to cmdbuf because the linker will default to it. */ const char *n = (*global.params.objfiles)[0]; n = FileName::name(n); global.params.exefile = (char *)FileName::forceExt(n, "exe"); } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); cmdbuf.writeByte(','); if (global.params.mapfile) writeFilename(&cmdbuf, global.params.mapfile); else if (global.params.map) { const char *fn = FileName::forceExt(global.params.exefile, "map"); const char *path = FileName::path(global.params.exefile); const char *p; if (path[0] == '\0') p = FileName::combine(global.params.objdir, fn); else p = fn; writeFilename(&cmdbuf, p); } else cmdbuf.writestring("nul"); cmdbuf.writeByte(','); for (size_t i = 0; i < global.params.libfiles->dim; i++) { if (i) cmdbuf.writeByte('+'); writeFilename(&cmdbuf, (*global.params.libfiles)[i]); } if (global.params.deffile) { cmdbuf.writeByte(','); writeFilename(&cmdbuf, global.params.deffile); } /* Eliminate unnecessary trailing commas */ while (1) { size_t i = cmdbuf.offset; if (!i || cmdbuf.data[i - 1] != ',') break; cmdbuf.offset--; } if (global.params.resfile) { cmdbuf.writestring("/RC:"); writeFilename(&cmdbuf, global.params.resfile); } if (global.params.map || global.params.mapfile) cmdbuf.writestring("/m"); #if 0 if (debuginfo) cmdbuf.writestring("/li"); if (codeview) { cmdbuf.writestring("/co"); if (codeview3) cmdbuf.writestring(":3"); } #else if (global.params.symdebug) cmdbuf.writestring("/co"); #endif cmdbuf.writestring("/noi"); for (size_t i = 0; i < global.params.linkswitches->dim; i++) { cmdbuf.writestring((*global.params.linkswitches)[i]); } cmdbuf.writeByte(';'); char *p = cmdbuf.peekString(); const char *lnkfilename = NULL; size_t plen = strlen(p); if (plen > 7000) { lnkfilename = FileName::forceExt(global.params.exefile, "lnk"); File flnk(lnkfilename); flnk.setbuffer(p, plen); flnk.ref = 1; if (flnk.write()) error(Loc(), "error writing file %s", lnkfilename); if (strlen(lnkfilename) < plen) sprintf(p, "@%s", lnkfilename); } const char *linkcmd = getenv("LINKCMD"); if (!linkcmd) linkcmd = "link"; int status = executecmd(linkcmd, p); if (lnkfilename) { remove(lnkfilename); FileName::free(lnkfilename); } return status; } #elif __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun pid_t childpid; int status; // Build argv[] Strings argv; const char *cc = getenv("CC"); if (!cc) cc = "gcc"; argv.push(cc); argv.insert(1, global.params.objfiles); #if __APPLE__ // If we are on Mac OS X and linking a dynamic library, // add the "-dynamiclib" flag if (global.params.dll) argv.push("-dynamiclib"); #elif __linux__ || __FreeBSD__ || __OpenBSD__ || __sun if (global.params.dll) argv.push("-shared"); #endif // None of that a.out stuff. Use explicit exe file name, or // generate one from name of first source file. argv.push("-o"); if (global.params.exefile) { argv.push(global.params.exefile); } else if (global.params.run) { #if 1 char name[L_tmpnam + 14 + 1]; strcpy(name, P_tmpdir); strcat(name, "/dmd_runXXXXXX"); int fd = mkstemp(name); if (fd == -1) { error(Loc(), "error creating temporary file"); return 1; } else close(fd); global.params.exefile = mem.strdup(name); argv.push(global.params.exefile); #else /* The use of tmpnam raises the issue of "is this a security hole"? * The hole is that after tmpnam and before the file is opened, * the attacker modifies the file system to get control of the * file with that name. I do not know if this is an issue in * this context. * We cannot just replace it with mkstemp, because this name is * passed to the linker that actually opens the file and writes to it. */ char s[L_tmpnam + 1]; char *n = tmpnam(s); global.params.exefile = mem.strdup(n); argv.push(global.params.exefile); #endif } else { // Generate exe file name from first obj name const char *n = (*global.params.objfiles)[0]; char *ex; n = FileName::name(n); const char *e = FileName::ext(n); if (e) { e--; // back up over '.' ex = (char *)mem.malloc(e - n + 1); memcpy(ex, n, e - n); ex[e - n] = 0; // If generating dll then force dll extension if (global.params.dll) ex = (char *)FileName::forceExt(ex, global.dll_ext); } else ex = (char *)"a.out"; // no extension, so give up argv.push(ex); global.params.exefile = ex; } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); if (global.params.symdebug) argv.push("-g"); if (global.params.is64bit) argv.push("-m64"); else argv.push("-m32"); if (global.params.map || global.params.mapfile) { argv.push("-Xlinker"); #if __APPLE__ argv.push("-map"); #else argv.push("-Map"); #endif if (!global.params.mapfile) { const char *fn = FileName::forceExt(global.params.exefile, "map"); const char *path = FileName::path(global.params.exefile); const char *p; if (path[0] == '\0') p = FileName::combine(global.params.objdir, fn); else p = fn; global.params.mapfile = (char *)p; } argv.push("-Xlinker"); argv.push(global.params.mapfile); } if (0 && global.params.exefile) { /* This switch enables what is known as 'smart linking' * in the Windows world, where unreferenced sections * are removed from the executable. It eliminates unreferenced * functions, essentially making a 'library' out of a module. * Although it is documented to work with ld version 2.13, * in practice it does not, but just seems to be ignored. * Thomas Kuehne has verified that it works with ld 2.16.1. * BUG: disabled because it causes exception handling to fail * because EH sections are "unreferenced" and elided */ argv.push("-Xlinker"); argv.push("--gc-sections"); } for (size_t i = 0; i < global.params.linkswitches->dim; i++) { const char *p = (*global.params.linkswitches)[i]; if (!p || !p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L'))) // Don't need -Xlinker if switch starts with -l or -L. // Eliding -Xlinker is significant for -L since it allows our paths // to take precedence over gcc defaults. argv.push("-Xlinker"); argv.push(p); } /* Add each library, prefixing it with "-l". * The order of libraries passed is: * 1. any libraries passed with -L command line switch * 2. libraries specified on the command line * 3. libraries specified by pragma(lib), which were appended * to global.params.libfiles. * 4. standard libraries. */ for (size_t i = 0; i < global.params.libfiles->dim; i++) { const char *p = (*global.params.libfiles)[i]; size_t plen = strlen(p); if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a') argv.push(p); else { char *s = (char *)mem.malloc(plen + 3); s[0] = '-'; s[1] = 'l'; memcpy(s + 2, p, plen + 1); argv.push(s); } } for (size_t i = 0; i < global.params.dllfiles->dim; i++) { const char *p = (*global.params.dllfiles)[i]; argv.push(p); } /* Standard libraries must go after user specified libraries * passed with -l. */ const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; size_t slen = strlen(libname); if (slen) { char *buf = (char *)malloc(3 + slen + 1); strcpy(buf, "-l"); /* Use "-l:libname.a" if the library name is complete */ if (slen > 3 + 2 && memcmp(libname, "lib", 3) == 0 && (memcmp(libname + slen - 2, ".a", 2) == 0 || memcmp(libname + slen - 3, ".so", 3) == 0) ) { strcat(buf, ":"); } strcat(buf, libname); argv.push(buf); // turns into /usr/lib/libphobos2.a } #ifdef __sun argv.push("-mt"); #endif // argv.push("-ldruntime"); argv.push("-lpthread"); argv.push("-lm"); #if __linux__ // Changes in ld for Ubuntu 11.10 require this to appear after phobos2 argv.push("-lrt"); #endif if (global.params.verbose) { // Print it for (size_t i = 0; i < argv.dim; i++) fprintf(global.stdmsg, "%s ", argv[i]); fprintf(global.stdmsg, "\n"); } argv.push(NULL); // set up pipes int fds[2]; if (pipe(fds) == -1) { perror("Unable to create pipe to linker"); return -1; } childpid = fork(); if (childpid == 0) { // pipe linker stderr to fds[0] dup2(fds[1], STDERR_FILENO); close(fds[0]); execvp(argv[0], (char **)argv.tdata()); perror(argv[0]); // failed to execute return -1; } else if (childpid == -1) { perror("Unable to fork"); return -1; } close(fds[1]); const int nme = findNoMainError(fds[0]); waitpid(childpid, &status, 0); if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) { if (nme == -1) { perror("Error with the linker pipe"); return -1; } else { printf("--- errorlevel %d\n", status); if (nme == 1) error(Loc(), "no main function specified"); } } } else if (WIFSIGNALED(status)) { printf("--- killed by signal %d\n", WTERMSIG(status)); status = 1; } return status; #else printf ("Linking is not yet supported for this version of DMD.\n"); return -1; #endif }
Expression *arrayOp(BinExp *e, Scope *sc) { //printf("BinExp::arrayOp() %s\n", toChars()); Type *tb = e->type->toBasetype(); assert(tb->ty == Tarray || tb->ty == Tsarray); Type *tbn = tb->nextOf()->toBasetype(); if (tbn->ty == Tvoid) { e->error("cannot perform array operations on void[] arrays"); return new ErrorExp(); } if (!isArrayOpValid(e)) { e->error("invalid array operation %s (possible missing [])", e->toChars()); return new ErrorExp(); } Expressions *arguments = new Expressions(); /* The expression to generate an array operation for is mangled * into a name to use as the array operation function name. * Mangle in the operands and operators in RPN order, and type. */ OutBuffer buf; buf.writestring("_array"); buildArrayIdent(e, &buf, arguments); buf.writeByte('_'); /* Append deco of array element type */ buf.writestring(e->type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); char *name = buf.peekString(); Identifier *ident = Identifier::idPool(name); FuncDeclaration **pFd = (FuncDeclaration **)dmd_aaGet(&arrayfuncs, (void *)ident); FuncDeclaration *fd = *pFd; if (!fd) fd = buildArrayOp(ident, e, sc); if (fd && fd->errors) { const char *fmt; if (tbn->ty == Tstruct || tbn->ty == Tclass) fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations"; else if (!tbn->isscalar()) fmt = "invalid array operation '%s' because %s is not a scalar type"; else fmt = "invalid array operation '%s' for element type %s"; e->error(fmt, e->toChars(), tbn->toChars()); return new ErrorExp(); } *pFd = fd; Expression *ev = new VarExp(e->loc, fd); Expression *ec = new CallExp(e->loc, ev, arguments); return semantic(ec, sc); }
const char *inifile(const char *argv0x, const char *inifilex, const char *envsectionname) { char *argv0 = (char *)argv0x; char *inifile = (char *)inifilex; // do const-correct later char *path; // need path for @P macro char *filename; OutBuffer buf; int envsection = 0; size_t envsectionnamelen = strlen(envsectionname); #if LOG printf("inifile(argv0 = '%s', inifile = '%s')\n", argv0, inifile); #endif if (FileName::absolute(inifile)) { filename = inifile; } else { /* Look for inifile in the following sequence of places: * o current directory * o home directory * o directory off of argv0 * o SYSCONFDIR (default=/etc/) */ if (FileName::exists(inifile)) { filename = inifile; } else { filename = (char *)FileName::combine(getenv("HOME"), inifile); if (!FileName::exists(filename)) { #if _WIN32 // This fix by Tim Matthews char resolved_name[MAX_PATH + 1]; if(GetModuleFileName(NULL, resolved_name, MAX_PATH + 1) && FileName::exists(resolved_name)) { filename = (char *)FileName::replaceName(resolved_name, inifile); if(FileName::exists(filename)) goto Ldone; } #endif filename = (char *)FileName::replaceName(argv0, inifile); if (!FileName::exists(filename)) { #if __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #if __GLIBC__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun // This fix by Thomas Kuehne /* argv0 might be a symbolic link, * so try again looking past it to the real path */ #if __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun char resolved_name[PATH_MAX + 1]; char* real_argv0 = realpath(argv0, resolved_name); #else char* real_argv0 = realpath(argv0, NULL); #endif //printf("argv0 = %s, real_argv0 = %p\n", argv0, real_argv0); if (real_argv0) { filename = (char *)FileName::replaceName(real_argv0, inifile); #if __linux__ free(real_argv0); #endif if (FileName::exists(filename)) goto Ldone; } #else #error use of glibc non-standard extension realpath(char*, NULL) #endif if (1){ // Search PATH for argv0 const char *p = getenv("PATH"); #if LOG printf("\tPATH='%s'\n", p); #endif Strings *paths = FileName::splitPath(p); filename = (char *)FileName::searchPath(paths, argv0, 0); if (!filename) goto Letc; // argv0 not found on path filename = (char *)FileName::replaceName(filename, inifile); if (FileName::exists(filename)) goto Ldone; } // Search /etc/ for inifile Letc: #ifndef SYSCONFDIR # error SYSCONFDIR not defined #endif assert(SYSCONFDIR != NULL && strlen(SYSCONFDIR)); filename = (char *)FileName::combine((char *)SYSCONFDIR, inifile); #endif // __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun Ldone: ; } } } } path = (char *)FileName::path(filename); #if LOG printf("\tpath = '%s', filename = '%s'\n", path, filename); #endif File file(filename); if (file.read()) return filename; // error reading file // Parse into lines int eof = 0; for (size_t i = 0; i < file.len && !eof; i++) { size_t linestart = i; for (; i < file.len; i++) { switch (file.buffer[i]) { case '\r': break; case '\n': // Skip if it was preceded by '\r' if (i && file.buffer[i - 1] == '\r') goto Lskip; break; case 0: case 0x1A: eof = 1; break; default: continue; } break; } buf.reset(); // First, expand the macros. // Macros are bracketed by % characters. for (size_t k = 0; k < i - linestart; k++) { // The line is file.buffer[linestart..i] char *line = (char *)&file.buffer[linestart]; if (line[k] == '%') { for (size_t j = k + 1; j < i - linestart; j++) { if (line[j] == '%') { char *p = NULL; char *palloc = NULL; if (j - k == 3 && Port::memicmp(&line[k + 1], "@P", 2) == 0) { // %@P% is special meaning the path to the .ini file p = path; if (!*p) p = (char *)"."; } else { size_t len2 = j - k; char tmp[10]; // big enough most of the time if (len2 <= sizeof(tmp)) p = tmp; else { p = (char *)malloc(len2); palloc = p; } len2--; memcpy(p, &line[k + 1], len2); p[len2] = 0; Port::strupr(p); p = getenv(p); if (!p) p = (char *)""; } buf.writestring(p); if (palloc) free(palloc); k = j; goto L1; } } } buf.writeByte(line[k]); L1: ; } // Remove trailing spaces while (buf.offset && isspace(buf.data[buf.offset - 1])) buf.offset--; { char *p = buf.peekString(); // The expanded line is in p. // Now parse it for meaning. p = skipspace(p); switch (*p) { case ';': // comment case 0: // blank break; case '[': // look for [Environment] p = skipspace(p + 1); char *pn; for (pn = p; isalnum((utf8_t)*pn); pn++) ; if (pn - p == envsectionnamelen && Port::memicmp(p, envsectionname, envsectionnamelen) == 0 && *skipspace(pn) == ']' ) envsection = 1; else envsection = 0; break; default: if (envsection) { char *pn = p; // Convert name to upper case; // remove spaces bracketing = for (p = pn; *p; p++) { if (islower((utf8_t)*p)) *p &= ~0x20; else if (isspace((utf8_t)*p)) memmove(p, p + 1, strlen(p)); else if (*p == '=') { p++; while (isspace((utf8_t)*p)) memmove(p, p + 1, strlen(p)); break; } } putenv(strdup(pn)); #if LOG printf("\tputenv('%s')\n", pn); //printf("getenv(\"TEST\") = '%s'\n",getenv("TEST")); #endif } break; } } Lskip: ; } return filename; }
void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { IF_LOG Logger::println("CompoundAsmStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; // disable inlining by default if (!p->func()->decl->allowInlining) { p->func()->setNeverInline(); } // create asm block structure assert(!p->asmBlock); auto asmblock = new IRAsmBlock(stmt); assert(asmblock); p->asmBlock = asmblock; // do asm statements for (unsigned i = 0; i < stmt->statements->dim; i++) { if (Statement *s = (*stmt->statements)[i]) { Statement_toIR(s, p); } } // build forwarder for in-asm branches to external labels // this additional asm code sets the __llvm_jump_target variable // to a unique value that will identify the jump target in // a post-asm switch // maps each goto destination to its special value std::map<LabelDsymbol *, int> gotoToVal; // location of the special value determining the goto label // will be set if post-asm dispatcher block is needed LLValue *jump_target = nullptr; { FuncDeclaration *fd = gIR->func()->decl; OutBuffer mangleBuf; mangleToBuffer(fd, &mangleBuf); const char *fdmangle = mangleBuf.peekString(); // we use a simple static counter to make sure the new end labels are unique static size_t uniqueLabelsId = 0; std::ostringstream asmGotoEndLabel; printLabelName(asmGotoEndLabel, fdmangle, "_llvm_asm_end"); asmGotoEndLabel << uniqueLabelsId++; // initialize the setter statement we're going to build auto outSetterStmt = new IRAsmStmt; std::string asmGotoEnd = "\n\tjmp " + asmGotoEndLabel.str() + "\n"; std::ostringstream code; code << asmGotoEnd; int n_goto = 1; size_t n = asmblock->s.size(); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; // skip non-branch statements if (!a->isBranchToLabel) { continue; } // if internal, no special handling is necessary, skip std::vector<Identifier *>::const_iterator it, end; end = asmblock->internalLabels.end(); bool skip = false; for (auto il : asmblock->internalLabels) { if (il->equals(a->isBranchToLabel->ident)) { skip = true; } } if (skip) { continue; } // if we already set things up for this branch target, skip if (gotoToVal.find(a->isBranchToLabel) != gotoToVal.end()) { continue; } // record that the jump needs to be handled in the post-asm dispatcher gotoToVal[a->isBranchToLabel] = n_goto; // provide an in-asm target for the branch and set value IF_LOG Logger::println( "statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->ident->toChars()); printLabelName(code, fdmangle, a->isBranchToLabel->ident->toChars()); code << ":\n\t"; code << "movl $<<in" << n_goto << ">>, $<<out0>>\n"; // FIXME: Store the value -> label mapping somewhere, so it can be // referenced later outSetterStmt->in.push_back(DtoConstUint(n_goto)); outSetterStmt->in_c += "i,"; code << asmGotoEnd; ++n_goto; } if (code.str() != asmGotoEnd) { // finalize code outSetterStmt->code = code.str(); outSetterStmt->code += asmGotoEndLabel.str() + ":\n"; // create storage for and initialize the temporary jump_target = DtoAllocaDump(DtoConstUint(0), 0, "__llvm_jump_target"); // setup variable for output from asm outSetterStmt->out_c = "=*m,"; outSetterStmt->out.push_back(jump_target); asmblock->s.push_back(outSetterStmt); } else { delete outSetterStmt; } } // build a fall-off-end-properly asm statement FuncDeclaration *thisfunc = p->func()->decl; bool useabiret = false; p->asmBlock->asmBlock->abiret = nullptr; if (thisfunc->fbody->endsWithAsm() == stmt && thisfunc->type->nextOf()->ty != Tvoid) { // there can't be goto forwarders in this case assert(gotoToVal.empty()); emitABIReturnAsmStmt(asmblock, stmt->loc, thisfunc); useabiret = true; } // build asm block std::vector<LLValue *> outargs; std::vector<LLValue *> inargs; std::vector<LLType *> outtypes; std::vector<LLType *> intypes; std::string out_c; std::string in_c; std::string clobbers; std::string code; size_t asmIdx = asmblock->retn; Logger::println("do outputs"); size_t n = asmblock->s.size(); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; assert(a); size_t onn = a->out.size(); for (size_t j = 0; j < onn; ++j) { outargs.push_back(a->out[j]); outtypes.push_back(a->out[j]->getType()); } if (!a->out_c.empty()) { out_c += a->out_c; } remap_outargs(a->code, onn + a->in.size(), asmIdx); asmIdx += onn; } Logger::println("do inputs"); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; assert(a); size_t inn = a->in.size(); for (size_t j = 0; j < inn; ++j) { inargs.push_back(a->in[j]); intypes.push_back(a->in[j]->getType()); } if (!a->in_c.empty()) { in_c += a->in_c; } remap_inargs(a->code, inn + a->out.size(), asmIdx); asmIdx += inn; if (!code.empty()) { code += "\n\t"; } code += a->code; } asmblock->s.clear(); // append inputs out_c += in_c; // append clobbers for (const auto &c : asmblock->clobs) { out_c += c; } // remove excessive comma if (!out_c.empty()) { out_c.resize(out_c.size() - 1); } IF_LOG { Logger::println("code = \"%s\"", code.c_str()); Logger::println("constraints = \"%s\"", out_c.c_str()); } // build return types LLType *retty; if (asmblock->retn) { retty = asmblock->retty; } else { retty = llvm::Type::getVoidTy(gIR->context()); } // build argument types std::vector<LLType *> types; types.insert(types.end(), outtypes.begin(), outtypes.end()); types.insert(types.end(), intypes.begin(), intypes.end()); llvm::FunctionType *fty = llvm::FunctionType::get(retty, types, false); IF_LOG Logger::cout() << "function type = " << *fty << '\n'; std::vector<LLValue *> args; args.insert(args.end(), outargs.begin(), outargs.end()); args.insert(args.end(), inargs.begin(), inargs.end()); IF_LOG { Logger::cout() << "Arguments:" << '\n'; Logger::indent(); size_t i = 0; for (auto arg : args) { Stream cout = Logger::cout(); cout << '$' << i << " ==> " << *arg; if (!llvm::isa<llvm::Instruction>(arg) && !llvm::isa<LLGlobalValue>(arg)) { cout << '\n'; } ++i; } Logger::undent(); } llvm::InlineAsm *ia = llvm::InlineAsm::get(fty, code, out_c, true); llvm::CallInst *call = p->ir->CreateCall( ia, args, retty == LLType::getVoidTy(gIR->context()) ? "" : "asm"); IF_LOG Logger::cout() << "Complete asm statement: " << *call << '\n'; // capture abi return value if (useabiret) { IRAsmBlock *block = p->asmBlock; if (block->retfixup) { block->asmBlock->abiret = (*block->retfixup)(p->ir, call); } else if (p->asmBlock->retemu) { block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret); } else { block->asmBlock->abiret = call; } } p->asmBlock = nullptr; // if asm contained external branches, emit goto forwarder code if (!gotoToVal.empty()) { assert(jump_target); // make new blocks llvm::BasicBlock *bb = p->insertBB("afterasmgotoforwarder"); llvm::LoadInst *val = p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); llvm::SwitchInst *sw = p->ir->CreateSwitch(val, bb, gotoToVal.size()); // add all cases for (const auto &pair : gotoToVal) { llvm::BasicBlock *casebb = p->insertBBBefore(bb, "case"); sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), pair.second), casebb); p->scope() = IRScope(casebb); DtoGoto(stmt->loc, pair.first); } p->scope() = IRScope(bb); } }
void TryCatchScope::emitCatchBodies(IRState &irs, llvm::Value *ehPtrSlot) { assert(catchBlocks.empty()); auto &PGO = irs.funcGen().pgo; const auto entryCount = PGO.setCurrentStmt(stmt); struct CBPrototype { Type *t; llvm::BasicBlock *catchBB; uint64_t catchCount; uint64_t uncaughtCount; }; llvm::SmallVector<CBPrototype, 8> cbPrototypes; cbPrototypes.reserve(stmt->catches->dim); for (auto c : *stmt->catches) { auto catchBB = irs.insertBBBefore(endbb, llvm::Twine("catch.") + c->type->toChars()); irs.scope() = IRScope(catchBB); irs.DBuilder.EmitBlockStart(c->loc); PGO.emitCounterIncrement(c); bool isCPPclass = false; if (auto lp = c->langPlugin()) // CALYPSO lp->codegen()->toBeginCatch(irs, c); else { const auto cd = c->type->toBasetype()->isClassHandle(); isCPPclass = cd->isCPPclass(); const auto enterCatchFn = getRuntimeFunction( Loc(), irs.module, isCPPclass ? "__cxa_begin_catch" : "_d_eh_enter_catch"); const auto ptr = DtoLoad(ehPtrSlot); const auto throwableObj = irs.ir->CreateCall(enterCatchFn, ptr); // For catches that use the Throwable object, create storage for it. // We will set it in the code that branches from the landing pads // (there might be more than one) to catchBB. if (c->var) { // This will alloca if we haven't already and take care of nested refs // if there are any. DtoDeclarationExp(c->var); // Copy the exception reference over from the _d_eh_enter_catch return // value. DtoStore(DtoBitCast(throwableObj, DtoType(c->var->type)), getIrLocal(c->var)->value); } } // Emit handler, if there is one. The handler is zero, for instance, // when building 'catch { debug foo(); }' in non-debug mode. if (isCPPclass) { // from DMD: /* C++ catches need to end with call to __cxa_end_catch(). * Create: * try { handler } finally { __cxa_end_catch(); } * Note that this is worst case code because it always sets up an * exception handler. At some point should try to do better. */ FuncDeclaration *fdend = FuncDeclaration::genCfunc(nullptr, Type::tvoid, "__cxa_end_catch"); Expression *efunc = VarExp::create(Loc(), fdend); Expression *ecall = CallExp::create(Loc(), efunc); ecall->type = Type::tvoid; Statement *call = ExpStatement::create(Loc(), ecall); Statement *stmt = c->handler ? TryFinallyStatement::create(Loc(), c->handler, call) : call; Statement_toIR(stmt, &irs); } else { if (c->handler) Statement_toIR(c->handler, &irs); } if (!irs.scopereturned()) { // CALYPSO FIXME: _cxa_end_catch won't be called if it has already returned if (auto lp = c->langPlugin()) lp->codegen()->toEndCatch(irs, c); irs.ir->CreateBr(endbb); } irs.DBuilder.EmitBlockEnd(); // PGO information, currently unused auto catchCount = PGO.getRegionCount(c); // uncaughtCount is handled in a separate pass below cbPrototypes.push_back({c->type->toBasetype(), catchBB, catchCount, 0}); // CALYPSO } // Total number of uncaught exceptions is equal to the execution count at // the start of the try block minus the one after the continuation. // uncaughtCount keeps track of the exception type mismatch count while // iterating through the catch block prototypes in reversed order. auto uncaughtCount = entryCount - PGO.getRegionCount(stmt); for (auto it = cbPrototypes.rbegin(), end = cbPrototypes.rend(); it != end; ++it) { it->uncaughtCount = uncaughtCount; // Add this catch block's match count to the uncaughtCount, because these // failed to match the remaining (lexically preceding) catch blocks. uncaughtCount += it->catchCount; } catchBlocks.reserve(stmt->catches->dim); auto c_it = stmt->catches->begin(); // CALYPSO for (const auto &p : cbPrototypes) { auto branchWeights = PGO.createProfileWeights(p.catchCount, p.uncaughtCount); LLGlobalVariable *ci; if (auto lp = (*c_it)->langPlugin()) // CALYPSO ci = lp->codegen()->toCatchScopeType(irs, p.t); else { ClassDeclaration *cd = p.t->isClassHandle(); DtoResolveClass(cd); if (cd->isCPPclass()) { const char *name = Target::cppTypeInfoMangle(cd); auto cpp_ti = getOrCreateGlobal( cd->loc, irs.module, getVoidPtrType(), /*isConstant=*/true, LLGlobalValue::ExternalLinkage, /*init=*/nullptr, name); // Wrap std::type_info pointers inside a __cpp_type_info_ptr class instance so that // the personality routine may differentiate C++ catch clauses from D ones. OutBuffer mangleBuf; mangleBuf.writestring("_D"); mangleToBuffer(cd, &mangleBuf); mangleBuf.printf("%d%s", 18, "_cpp_type_info_ptr"); const auto wrapperMangle = getIRMangledVarName(mangleBuf.peekString(), LINKd); RTTIBuilder b(ClassDeclaration::cpp_type_info_ptr); b.push(cpp_ti); auto wrapperType = llvm::cast<llvm::StructType>( static_cast<IrTypeClass*>(ClassDeclaration::cpp_type_info_ptr->type->ctype)->getMemoryLLType()); auto wrapperInit = b.get_constant(wrapperType); ci = getOrCreateGlobal( cd->loc, irs.module, wrapperType, /*isConstant=*/true, LLGlobalValue::LinkOnceODRLinkage, wrapperInit, wrapperMangle); } else { ci = getIrAggr(cd)->getClassInfoSymbol(); } } catchBlocks.push_back({ci, p.catchBB, branchWeights}); c_it++; } }
void StaticAssert::semantic2(Scope *sc) { //printf("StaticAssert::semantic2() %s\n", toChars()); ScopeDsymbol *sd = new ScopeDsymbol(); sc = sc->push(sd); sc->flags |= SCOPEstaticassert; sc = sc->startCTFE(); Expression *e = exp->semantic(sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); sc = sc->pop(); // Simplify expression, to make error messages nicer if CTFE fails e = e->optimize(0); if (!e->type->checkBoolean()) { if (e->type->toBasetype() != Type::terror) exp->error("expression %s of type %s does not have a boolean value", exp->toChars(), e->type->toChars()); return; } unsigned olderrs = global.errors; e = e->ctfeInterpret(); if (global.errors != olderrs) { errorSupplemental(loc, "while evaluating: static assert(%s)", exp->toChars()); } else if (e->isBool(false)) { if (msg) { HdrGenState hgs; OutBuffer buf; sc = sc->startCTFE(); msg = msg->semantic(sc); msg = resolveProperties(sc, msg); sc = sc->endCTFE(); msg = msg->ctfeInterpret(); hgs.console = 1; StringExp * s = msg->toStringExp(); if (s) { s->postfix = 0; // Don't display a trailing 'c' msg = s; } msg->toCBuffer(&buf, &hgs); error("%s", buf.peekString()); } else error("(%s) is false", exp->toChars()); if (sc->tinst) sc->tinst->printInstantiationTrace(); if (!global.gag) fatal(); } else if (!e->isBool(true)) { error("(%s) is not evaluatable at compile time", exp->toChars()); } }