Skip to content

Commit

Permalink
emit better code for try-finally when function does not throw
Browse files Browse the repository at this point in the history
  • Loading branch information
WalterBright committed Nov 21, 2017
1 parent 9b8eba2 commit 7ab71d4
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 30 deletions.
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

// true if errors in semantic3 this function's frame ptr
bool semantic3Errors;
ForeachStatement *fes; // if foreach body, this is the foreach
Expand Down
15 changes: 13 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,11 @@ 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))
{
funcdecl.eh_none = true;
}

if (funcdecl.flags & FUNCFLAG.nothrowInprocess)
{
if (funcdecl.type == f)
Expand Down Expand Up @@ -1368,6 +1373,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 +1399,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 +1510,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
4 changes: 4 additions & 0 deletions src/ddmd/func.d
Original file line number Diff line number Diff line change
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 Expand Up @@ -1997,6 +2000,7 @@ extern (C++) class FuncDeclaration : Declaration
auto catches = new Catches();
catches.push(c);
sf = new TryCatchStatement(loc, s2, catches);
eh_none = false;
}
else
return null;
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;
}
}

0 comments on commit 7ab71d4

Please sign in to comment.