diff --git a/mak/COPY b/mak/COPY index 08ebdb881e..e152acef2c 100644 --- a/mak/COPY +++ b/mak/COPY @@ -408,6 +408,7 @@ COPY=\ $(IMPDIR)\rt\array\casting.d \ $(IMPDIR)\rt\array\capacity.d \ $(IMPDIR)\rt\array\concatenation.d \ + $(IMPDIR)\rt\array\utils.d \ \ $(IMPDIR)\rt\util\array.d \ \ diff --git a/mak/DOCS b/mak/DOCS index b6827ffffb..ce5c5c1613 100644 --- a/mak/DOCS +++ b/mak/DOCS @@ -85,6 +85,7 @@ DOCS=\ $(DOCDIR)\rt_array_construction.html \ $(DOCDIR)\rt_array_equality.html \ $(DOCDIR)\rt_array_concatenation.html \ + $(DOCDIR)\rt_array_utils.html \ \ $(DOCDIR)\rt_arrayassign.html \ $(DOCDIR)\rt_arraycat.html \ diff --git a/mak/SRCS b/mak/SRCS index b61114bb06..4d3a1200ab 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -456,6 +456,7 @@ SRCS=\ src\rt\array\casting.d \ src\rt\array\capacity.d \ src\rt\array\concatenation.d \ + src\rt\array\utils.d \ \ src\rt\backtrace\dwarf.d \ src\rt\backtrace\elf.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index 9805aa0f7e..ffe37833a2 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -1205,5 +1205,8 @@ $(IMPDIR)\rt\array\capacity.d : src\rt\array\capacity.d $(IMPDIR)\rt\array\concatenation.d : src\rt\array\concatenation.d copy $** $@ +$(IMPDIR)\rt\array\utils.d : src\rt\array\utils.d + copy $** $@ + $(IMPDIR)\rt\util\array.d : src\rt\util\array.d copy $** $@ diff --git a/src/rt/array/capacity.d b/src/rt/array/capacity.d index b518c3f1b9..9aca809f76 100644 --- a/src/rt/array/capacity.d +++ b/src/rt/array/capacity.d @@ -200,15 +200,6 @@ auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system private extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) nothrow pure; private extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) nothrow pure; -// This wrapper is needed because a externDFunc cannot be cast()ed directly. -private void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc -{ - import core.internal.traits : externDFunc; - - alias func = externDFunc!("rt.profilegc.accumulate", void function(string file, uint line, string funcname, string type, ulong sz) @nogc); - return func(file, line, funcname, type, sz); -} - /* * This template is needed because there need to be a `_d_arraysetlengthTTrace!Tarr` instance for every * `_d_arraysetlengthT!Tarr`. By wrapping both of these functions inside of this template we force the @@ -218,6 +209,10 @@ private void accumulate(string file, uint line, string funcname, string type, ul /// Implementation of `_d_arraysetlengthT` and `_d_arraysetlengthTTrace` template _d_arraysetlengthTImpl(Tarr : T[], T) { + import rt.array.utils : HookTraceImpl; + + private enum errorMessage = "Cannot resize arrays if compiling without support for runtime type information!"; + /** * Resize dynamic array * Params: @@ -243,54 +238,17 @@ template _d_arraysetlengthTImpl(Tarr : T[], T) return arr.length; } else - assert(0, "Cannot resize arrays if compiling without support for runtime type information!"); + assert(0, errorMessage); } - /** * TraceGC wrapper around $(REF _d_arraysetlengthT, rt,array,rt.array.capacity). * Bugs: - * The safety level of this function is faked. It shows itself as `@trusted pure nothrow` to not break existing code. + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. */ - size_t _d_arraysetlengthTTrace(string file, int line, string funcname, return scope ref Tarr arr, size_t newlength) @trusted pure nothrow - { - version (D_TypeInfo) - { - pragma(inline, false); - import core.memory : GC; - - auto accumulate = cast(void function(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow pure)&accumulate; - auto gcStats = cast(GC.Stats function() nothrow pure)&GC.stats; - - string name = Tarr.stringof; - - // FIXME: use rt.tracegc.accumulator when it is accessable in the future. - version (tracegc) - { - import core.stdc.stdio; - - printf("%s file = '%.*s' line = %d function = '%.*s' type = %.*s\n", - __FUNCTION__.ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr, - name.length, name.ptr - ); - } - - ulong currentlyAllocated = gcStats().allocatedInCurrentThread; - - scope(exit) - { - ulong size = gcStats().allocatedInCurrentThread - currentlyAllocated; - if (size > 0) - accumulate(file, line, funcname, name, size); - } - return _d_arraysetlengthT(arr, newlength); - } - else - assert(0, "Cannot resize arrays if compiling without support for runtime type information!"); - } + alias _d_arraysetlengthTTrace = HookTraceImpl!(Tarr, _d_arraysetlengthT, errorMessage); } @safe unittest diff --git a/src/rt/array/concatenation.d b/src/rt/array/concatenation.d index f36f391168..11d1f38f89 100644 --- a/src/rt/array/concatenation.d +++ b/src/rt/array/concatenation.d @@ -11,18 +11,13 @@ module rt.array.concatenation; /// See $(REF _d_arraycatnTX, rt,lifetime) private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) pure nothrow; -// This wrapper is needed because a externDFunc cannot be cast()ed directly. -private void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc -{ - import core.internal.traits : externDFunc; - - alias func = externDFunc!("rt.profilegc.accumulate", void function(string file, uint line, string funcname, string type, ulong sz) @nogc); - return func(file, line, funcname, type, sz); -} - /// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace` template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) { + import rt.array.utils : HookTraceImpl; + + private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!"; + /** * Concatenating the arrays inside of `arrs`. * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`. @@ -47,7 +42,7 @@ template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) return (cast(T*)result.ptr)[0 .. result.length]; } else - assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); + assert(0, errorMessage); } /** @@ -57,44 +52,7 @@ template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) * purity, and throwabilty checks. To prevent breaking existing code, this function template * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. */ - ResultArrT _d_arraycatnTXTrace(string file, int line, string funcname, scope const Tarr arrs) @trusted pure nothrow - { - pragma(inline, false); - version (D_TypeInfo) - { - import core.memory : GC; - auto accumulate = cast(void function(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow pure)&accumulate; - auto gcStats = cast(GC.Stats function() nothrow pure)&GC.stats; - - string name = ResultArrT.stringof; - - // FIXME: use rt.tracegc.accumulator when it is accessable in the future. - version (tracegc) - { - import core.stdc.stdio; - - printf("%s file = '%.*s' line = %d function = '%.*s' type = %.*s\n", - __FUNCTION__.ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr, - name.length, name.ptr - ); - } - - ulong currentlyAllocated = gcStats().allocatedInCurrentThread; - - scope(exit) - { - ulong size = gcStats().allocatedInCurrentThread - currentlyAllocated; - if (size > 0) - accumulate(file, line, funcname, name, size); - } - return _d_arraycatnTX(arrs); - } - else - assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); - } + alias _d_arraycatnTXTrace = HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage); } @safe unittest diff --git a/src/rt/array/utils.d b/src/rt/array/utils.d new file mode 100644 index 0000000000..97af8a9855 --- /dev/null +++ b/src/rt/array/utils.d @@ -0,0 +1,94 @@ +/** + This module contains utility functions to help the implementation of the runtime hook + + Copyright: Copyright Digital Mars 2000 - 2019. + License: Distributed under the + $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + (See accompanying file LICENSE) + Source: $(DRUNTIMESRC rt/_array/_utils.d) +*/ +module rt.array.utils; + +import core.internal.traits : Parameters; + +private auto gcStatsPure() nothrow pure +{ + import core.memory : GC; + + auto impureBypass = cast(GC.Stats function() pure nothrow)&GC.stats; + return impureBypass(); +} + +private ulong accumulatePure(string file, int line, string funcname, string name, ulong size) nothrow pure +{ + static ulong impureBypass(string file, int line, string funcname, string name, ulong size) @nogc nothrow + { + import core.internal.traits : externDFunc; + + alias accumulate = externDFunc!("rt.profilegc.accumulate", void function(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow); + accumulate(file, line, funcname, name, size); + return size; + } + + auto func = cast(ulong function(string file, int line, string funcname, string name, ulong size) @nogc nothrow pure)&impureBypass; + return func(file, line, funcname, name, size); +} + +/** + * TraceGC wrapper around runtime hook `Hook`. + * Params: + * T = Type of hook to report to accumulate + * Hook = The hook to wrap + * errorMessage = The error message incase `version != D_TypeInfo` + * file = File that called `HookTraceImpl` + * line = Line inside of `file` that called `HookTraceImpl` + * funcname = Function that called `HookTraceImpl` + * parameters = Parameters that will be used to call `Hook` + * Bugs: + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. +*/ +auto HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, string funcname, Parameters!Hook parameters) @trusted pure nothrow +{ + version (D_TypeInfo) + { + pragma(inline, false); + string name = T.stringof; + + // FIXME: use rt.tracegc.accumulator when it is accessable in the future. + version (tracegc) + { + import core.stdc.stdio; + + printf("%sTrace file = '%.*s' line = %d function = '%.*s' type = %.*s\n", + Hook.stringof.ptr, + file.length, file.ptr, + line, + funcname.length, funcname.ptr, + name.length, name.ptr + ); + } + + ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread; + + scope(exit) + { + ulong size = gcStatsPure().allocatedInCurrentThread - currentlyAllocated; + if (size > 0) + if (!accumulatePure(file, line, funcname, name, size)) { + // This 'if' and 'assert' is needed to force the compiler to not remove the call to + // `accumulatePure`. It really want to do that while optimizing as the function is + // `pure` and it does not influence the result of this hook. + + // `accumulatePure` returns the value of `size`, which can never be zero due to the + // previous 'if'. So this assert will never be triggered. + assert(0); + } + } + return Hook(parameters); + } + else + assert(0, errorMessage); +} + diff --git a/src/rt/profilegc.d b/src/rt/profilegc.d index fedea86964..182d6b8242 100644 --- a/src/rt/profilegc.d +++ b/src/rt/profilegc.d @@ -45,7 +45,7 @@ extern (C) void profilegc_setlogfilename(string name) logfilename = name ~ "\0"; } -public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc +public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow { if (sz == 0) return;