Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

emit better code for try-finally when function does not throw #7333

Merged
merged 1 commit into from
Nov 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/ddmd/backend/cc.d
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ enum
BFLnostackopt = 8, // set when stack elimination should not
// be done
// NTEXCEPTIONS
BFLehcode = 0x10, // set when we need to load exception code
BFLunwind = 0x1000, // do local_unwind following block
BFLehcode = 0x10, // BC_filter: need to load exception code
BFLunwind = 0x1000, // do local_unwind following block (unused)

BFLnomerg = 0x20, // do not merge with other blocks
BFLprolog = 0x80, // generate function prolog
Expand Down Expand Up @@ -668,6 +668,7 @@ enum
Fnotailrecursion = 0x4000, // no tail recursion optimizations
Ffakeeh = 0x8000, // allocate space for NT EH context sym anyway
Fnothrow = 0x10000, // function does not throw (even if not marked 'nothrow')
Feh_none = 0x20000, // ehmethod==EH_NONE for this function only
}

struct func_t
Expand Down
17 changes: 15 additions & 2 deletions src/ddmd/backend/cc.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,8 @@ enum
BFLnostackopt = 8, // set when stack elimination should not
// be done
#if NTEXCEPTIONS
BFLehcode = 0x10, // set when we need to load exception code
BFLunwind = 0x1000, // do local_unwind following block
BFLehcode = 0x10, // BC_filter: need to load exception code
BFLunwind = 0x1000, // do local_unwind following block (unused)
#endif
BFLnomerg = 0x20, // do not merge with other blocks
BFLprolog = 0x80, // generate function prolog
Expand Down Expand Up @@ -709,6 +709,7 @@ enum
Fnotailrecursion = 0x4000, // no tail recursion optimizations
Ffakeeh = 0x8000, // allocate space for NT EH context sym anyway
Fnothrow = 0x10000, // function does not throw (even if not marked 'nothrow')
Feh_none = 0x20000, // ehmethod==EH_NONE for this function only
};

struct func_t
Expand Down Expand Up @@ -1412,6 +1413,18 @@ struct Aliassym : Symbol { };
inline char *prettyident(Symbol *s) { return s->Sident; }
#endif

/************************
* Params:
* f = function symbol
* Returns:
* exception method for f
*/
inline EHmethod ehmethod(Symbol *f)
{
return f->Sfunc->Fflags3 & Feh_none ? EH_NONE : config.ehmethod;
}



/**********************************
* Function parameters:
Expand Down
36 changes: 23 additions & 13 deletions src/ddmd/backend/cod3.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/ddmd/backend/cod3.c, backend/cod3.c)
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/ddmd/backend/cod3.c
*/

#if !SPP
Expand Down Expand Up @@ -825,7 +826,8 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
break;
}
#if MARS
case BCjcatch:
case BCjcatch: // D catch clause of try-catch
assert(ehmethod(funcsym_p) != EH_NONE);
// Mark all registers as destroyed. This will prevent
// register assignments to variables used in catch blocks.
getregs(cdb,lpadregs());
Expand All @@ -843,7 +845,7 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
goto case_goto;
#endif
#if SCPP
case BCcatch:
case BCcatch: // C++ catch clause of try-catch
// Mark all registers as destroyed. This will prevent
// register assignments to variables used in catch blocks.
getregs(cdb,allregs | mES);
Expand All @@ -859,7 +861,7 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
nextb = bl->nthSucc(0);
if ((MARS ||
funcsym_p->Sfunc->Fflags3 & Fnteh) &&
config.ehmethod != EH_DWARF &&
ehmethod(funcsym_p) != EH_DWARF &&
bl->Btry != nextb->Btry &&
nextb->BC != BC_finally)
{
Expand All @@ -878,7 +880,8 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
}
}
#endif
if (config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH)
if (config.ehmethod == EH_WIN32 && !(funcsym_p->Sfunc->Fflags3 & Feh_none) ||
config.ehmethod == EH_SEH)
{
nteh_unwind(cdb,0,toindex);
}
Expand Down Expand Up @@ -941,8 +944,12 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
}

case BC_try:
if (config.ehmethod == EH_NONE)
if (config.ehmethod == EH_NONE || funcsym_p->Sfunc->Fflags3 & Feh_none)
{
/* Need to use frame pointer to access locals, not the stack pointer,
* because we'll be calling the BC_finally blocks and the stack will be off.
*/
usednteh |= EHtry;
}
else if (config.ehmethod == EH_SEH || config.ehmethod == EH_WIN32)
{
Expand All @@ -954,7 +961,7 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
goto case_goto;

case BC_finally:
if (config.ehmethod == EH_DWARF)
if (ehmethod(funcsym_p) == EH_DWARF)
{
// Mark scratch registers as destroyed.
getregsNoSave(lpadregs());
Expand All @@ -969,7 +976,8 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
}
else
{
if (config.ehmethod == EH_SEH || config.ehmethod == EH_WIN32)
if (config.ehmethod == EH_SEH ||
config.ehmethod == EH_WIN32 && !(funcsym_p->Sfunc->Fflags3 & Feh_none))
{
// Mark all registers as destroyed. This will prevent
// register assignments to variables used in finally blocks.
Expand All @@ -988,7 +996,7 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls

case BC_lpad:
{
assert(config.ehmethod == EH_DWARF);
assert(ehmethod(funcsym_p) == EH_DWARF);
// Mark all registers as destroyed. This will prevent
// register assignments to variables used in finally blocks.
getregsNoSave(lpadregs());
Expand All @@ -1005,7 +1013,7 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
{
regm_t retregs = 0;
gencodelem(cdb,e,&retregs,TRUE);
if (config.ehmethod == EH_DWARF)
if (ehmethod(funcsym_p) == EH_DWARF)
{
}
else
Expand Down Expand Up @@ -1102,7 +1110,8 @@ void outblkexitcode(CodeBuilder& cdb, block *bl, int& anyspill, const char* sfls
continue;
}
#endif
if (config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH)
if (config.ehmethod == EH_WIN32 && !(funcsym_p->Sfunc->Fflags3 & Feh_none) ||
config.ehmethod == EH_SEH)
{
if (bt->Bscope_index == 0)
{
Expand Down Expand Up @@ -2979,6 +2988,7 @@ void prolog_16bit_windows_farfunc(CodeBuilder& cdb, tym_t* tyf, bool* pushds)

void prolog_frame(CodeBuilder& cdb, unsigned farfunc, unsigned* xlocalsize, bool* enter, int* cfa_offset)
{
//printf("prolog_frame\n");
*cfa_offset = 0;

if (0 && config.exe == EX_WIN64)
Expand All @@ -3000,7 +3010,7 @@ void prolog_frame(CodeBuilder& cdb, unsigned farfunc, unsigned* xlocalsize, bool
(*xlocalsize >= 0x1000 && config.exe & EX_flat) ||
localsize >= 0x10000 ||
#if NTEXCEPTIONS == 2
(usednteh & ~NTEHjmonitor && (config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH)) ||
(usednteh & ~NTEHjmonitor && (config.ehmethod == EH_WIN32 && !(funcsym_p->Sfunc->Fflags3 & Feh_none) || config.ehmethod == EH_SEH)) ||
#endif
(config.target_cpu >= TARGET_80386 &&
config.flags4 & CFG4speed)
Expand All @@ -3014,7 +3024,7 @@ void prolog_frame(CodeBuilder& cdb, unsigned farfunc, unsigned* xlocalsize, bool
// Don't reorder instructions, as dwarf CFA relies on it
code_orflag(cdb.last(), CFvolatile);
#if NTEXCEPTIONS == 2
if (usednteh & ~NTEHjmonitor && (config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH))
if (usednteh & ~NTEHjmonitor && (config.ehmethod == EH_WIN32 && !(funcsym_p->Sfunc->Fflags3 & Feh_none) || config.ehmethod == EH_SEH))
{
nteh_prolog(cdb);
int sz = nteh_contextsym_size();
Expand Down Expand Up @@ -3365,7 +3375,7 @@ void prolog_trace(CodeBuilder& cdb, bool farfunc, unsigned* regsaved)
char name[IDMAX+IDOHD+1];
size_t len = objmod->mangle(funcsym_p,name);
assert(len < sizeof(name));
cdb.genasm((unsigned char *)name,len); // append func name
cdb.genasm((char *)name,len); // append func name
#endif
*regsaved = s->Sregsaved;
}
Expand Down
8 changes: 7 additions & 1 deletion src/ddmd/backend/dwarf.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/ddmd/backend/dwarf.c, backend/dwarf.c)
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/ddmd/backend/dwarf.c
*/

// Emit Dwarf symbolic debug info
Expand Down Expand Up @@ -92,6 +93,11 @@ static char __file__[] = __FILE__; // for tassert.h
*/
bool doUnwindEhFrame()
{
if (funcsym_p->Sfunc->Fflags3 & Feh_none)
{
return (config.exe & (EX_FREEBSD | EX_FREEBSD64)) != 0;
}

/* FreeBSD fails when having some frames as having unwinding info and some not.
* (It hangs in unittests for std.datetime.)
* g++ on FreeBSD does not generate mixed frames, while g++ on OSX and Linux does.
Expand Down Expand Up @@ -1530,7 +1536,7 @@ void dwarf_func_term(Symbol *sfunc)

unsigned funcabbrevcode;

if (config.ehmethod == EH_DM)
if (ehmethod(sfunc) == EH_DM)
{
IDXSEC dfseg = dwarf_getsegment(debug_frame_name, 1);
writeDebugFrameFDE(dfseg, sfunc);
Expand Down
2 changes: 1 addition & 1 deletion src/ddmd/backend/nteh.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ code *nteh_patchindex(code* c, int sindex)

void nteh_gensindex(CodeBuilder& cdb, int sindex)
{
if (!(config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH))
if (!(config.ehmethod == EH_WIN32 || config.ehmethod == EH_SEH) || funcsym_p->Sfunc->Fflags3 & Feh_none)
return;
// Generate:
// MOV -4[EBP],sindex
Expand Down
3 changes: 3 additions & 0 deletions src/ddmd/declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ void builtin_init();
#define FUNCFLAGreturnInprocess 0x10 // working on inferring 'return' for parameters
#define FUNCFLAGinlineScanned 0x20 // function has been scanned for inline possibilities
#define FUNCFLAGinferScope 0x40 // infer 'scope' for parameters
#define FUNCFLAGhasCatches 0x80 // function has try-catch statements

class FuncDeclaration : public Declaration
{
Expand Down Expand Up @@ -523,6 +524,8 @@ class FuncDeclaration : public Declaration
CompiledCtfeFunction *ctfeCode; // Compiled code for interpreter
int inlineNest; // !=0 if nested inline
bool isArrayOp; // true if array operation
bool eh_none; /// true if no exception unwinding is needed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmh, that doesn't belong in the frontend, any other place you could store or retrieve this flag?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag is per-function, and is detected by the font end. It is later transferred to the backend's symbol for that same function.


// true if errors in semantic3 this function's frame ptr
bool semantic3Errors;
ForeachStatement *fes; // if foreach body, this is the foreach
Expand Down
19 changes: 17 additions & 2 deletions src/ddmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ extern(C++) final class Semantic2Visitor : Visitor

override void visit(StaticAssert sa)
{
//printf("StaticAssert::semantic2() %s\n", toChars());
//printf("StaticAssert::semantic2() %s\n", sa.toChars());
auto sds = new ScopeDsymbol();
sc = sc.push(sds);
sc.tinst = null;
Expand Down Expand Up @@ -1197,6 +1197,15 @@ extern(C++) final class Semantic3Visitor : Visitor
if (f.isnothrow && blockexit & BE.throw_)
error(funcdecl.loc, "nothrow %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars());

if (!(blockexit & BE.throw_ || funcdecl.flags & FUNCFLAG.hasCatches))
{
/* Disable optimization on Win32 due to
* https://issues.dlang.org/show_bug.cgi?id=17997
*/
if (!global.params.isWindows || global.params.is64bit)
funcdecl.eh_none = true; // don't generate unwind tables for this function
}

if (funcdecl.flags & FUNCFLAG.nothrowInprocess)
{
if (funcdecl.type == f)
Expand Down Expand Up @@ -1368,6 +1377,8 @@ extern(C++) final class Semantic3Visitor : Visitor
freq = freq.statementSemantic(sc2);
freq.blockExit(funcdecl, false);

funcdecl.eh_none = false;

sc2 = sc2.pop();

if (!global.params.useIn)
Expand All @@ -1392,6 +1403,8 @@ extern(C++) final class Semantic3Visitor : Visitor
fens = fens.statementSemantic(sc2);
fens.blockExit(funcdecl, false);

funcdecl.eh_none = false;

sc2 = sc2.pop();

if (!global.params.useOut)
Expand Down Expand Up @@ -1501,7 +1514,9 @@ extern(C++) final class Semantic3Visitor : Visitor
s = s.statementSemantic(sc2);

bool isnothrow = f.isnothrow & !(funcdecl.flags & FUNCFLAG.nothrowInprocess);
int blockexit = s.blockExit(funcdecl, isnothrow);
const blockexit = s.blockExit(funcdecl, isnothrow);
if (blockexit & BE.throw_)
funcdecl.eh_none = false;
if (f.isnothrow && isnothrow && blockexit & BE.throw_)
error(funcdecl.loc, "nothrow %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars());
if (funcdecl.flags & FUNCFLAG.nothrowInprocess && blockexit & BE.throw_)
Expand Down
4 changes: 2 additions & 2 deletions src/ddmd/eh.d
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ private @property @nogc nothrow auto NPTRSIZE() { return _tysize[TYnptr]; }
Symbol *except_gentables()
{
//printf("except_gentables()\n");
if (config.ehmethod == EHmethod.EH_DM)
if (config.ehmethod == EHmethod.EH_DM && !(funcsym_p.Sfunc.Fflags3 & Feh_none))
{
// BUG: alloca() changes the stack size, which is not reflected
// in the fixed eh tables.
if (Alloca.size)
error(null, 0, 0, "cannot mix core.std.stdlib.alloca() and exception handling in %s()", &funcsym_p.Sident[0]);

char[13+5+1] name;
char[13+5+1] name = void;
__gshared int tmpnum;
sprintf(name.ptr,"_HandlerTable%d",tmpnum++);

Expand Down
5 changes: 4 additions & 1 deletion src/ddmd/func.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/***
* Compiler implementation of the
* $(LINK2 http://www.dlang.org, D programming language).
*
Expand Down Expand Up @@ -136,6 +136,7 @@ public:
catches.push(ctch);

Statement s2 = new TryCatchStatement(Loc(), s._body, catches);
fd.eh_none = false;
replaceCurrent(s2);
s2.accept(this);
}
Expand All @@ -153,6 +154,7 @@ enum FUNCFLAG : uint
returnInprocess = 0x10, /// working on inferring 'return' for parameters
inlineScanned = 0x20, /// function has been scanned for inline possibilities
inferScope = 0x40, /// infer 'scope' for parameters
hasCatches = 0x80, /// function has try-catch statements
}

/***********************************************************
Expand Down Expand Up @@ -198,6 +200,7 @@ extern (C++) class FuncDeclaration : Declaration
CompiledCtfeFunction* ctfeCode; /// Compiled code for interpreter (not actually)
int inlineNest; /// !=0 if nested inline
bool isArrayOp; /// true if array operation
bool eh_none; /// true if no exception unwinding is needed

bool semantic3Errors; /// true if errors in semantic3 this function's frame ptr
ForeachStatement fes; /// if foreach body, this is the foreach
Expand Down
7 changes: 6 additions & 1 deletion src/ddmd/glue.d
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,10 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
if (fd.isVirtual() && (fd.fensure || fd.frequire))
f.Fflags3 |= Ffakeeh;

if (fd.eh_none)
// Same as config.ehmethod==EH_NONE, but only for this function
f.Fflags3 |= Feh_none;

s.Sclass = global.params.isOSX ? SCcomdat : SCglobal;
for (Dsymbol p = fd.parent; p; p = p.parent)
{
Expand Down Expand Up @@ -1246,7 +1250,8 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
}
}
}
insertFinallyBlockCalls(f.Fstartblock);
if (!(f.Fflags3 & Feh_none))
insertFinallyBlockCalls(f.Fstartblock);
}

// If static constructor
Expand Down
4 changes: 3 additions & 1 deletion src/ddmd/irstate.d
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ struct IRState
* the best we can do.
* Nothrow needs to be tracked at the Statement level.
*/
return !global.params.useExceptions;
FuncDeclaration fd;
return !global.params.useExceptions ||
(fd = getFunc()) !is null && fd.eh_none;
}
}