Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mak/COPY
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
\
Expand Down
1 change: 1 addition & 0 deletions mak/DOCS
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions mak/SRCS
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
3 changes: 3 additions & 0 deletions mak/WINDOWS
Original file line number Diff line number Diff line change
Expand Up @@ -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 $** $@
60 changes: 9 additions & 51 deletions src/rt/array/capacity.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down
54 changes: 6 additions & 48 deletions src/rt/array/concatenation.d
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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);
}

/**
Expand All @@ -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
Expand Down
94 changes: 94 additions & 0 deletions src/rt/array/utils.d
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, reading this I think there's may be a deeper issue whenever we have 'fake' pure code which internally calls impure code.

I guess the intent of such code is to not influence purity of the caller (i.e. completely optimizing away the caller would be fine), but the callee is not really pure, so it should not be removed on it's own. I wonder whether this can be expressed in any theoretically sound way, @tgehr any insights here?

It seems to me that the problem is that we lie to the compiler and simply tell it this function is pure. Maybe we should rather tell it Ignore purity checks for this call, but the function is not pure. Intuitively I'd say a

pure void foo()
{
    __traits(forcepure, bar());
}

Should work: if bar is not inlined, this would tell the compiler to keep the call, but ignore purity for purity calculation of the callee. With inlining, the compiler should be able to see the side effects anyway and should not optimize these away.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can see how your assert code has the same effect though: It marks the return value as used, as long as the call is not inlined. When it's inlined, the compiler may remove the assert, but it will see the impure parts of accumulatePure and keep these. This can probably also somehow solved like this: https://stackoverflow.com/questions/40122141/preventing-compiler-optimizations-while-benchmarking, but I'd prefer a compiler hook which should also work for void functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been thinking about introducing HookCallExp or a bool in CallExp that ignore the purity and somehow allowed the code to be inline, which is disallowed right now pragma(inline, false), whilst being able to be detected as the hook (HookCompoundStatement?) so the CTFE intercept code works.

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds like a great idea.

Copy link

Choose a reason for hiding this comment

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

BTW, reading this I think there's may be a deeper issue whenever we have 'fake' pure code which internally calls impure code.

I guess the intent of such code is to not influence purity of the caller (i.e. completely optimizing away the caller would be fine), but the callee is not really pure, so it should not be removed on it's own. I wonder whether this can be expressed in any theoretically sound way, @tgehr any insights here?
...

@jpf91 This was what __mutable functions were for in the original __mutable proposal. Inlining for such functions is not so easy if you want to preserve constraints on evaluation order precisely.

}
}
return Hook(parameters);
}
else
assert(0, errorMessage);
}

2 changes: 1 addition & 1 deletion src/rt/profilegc.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down