From 3c9bea436f4ebdba018ef1a787f71b560adcdb15 Mon Sep 17 00:00:00 2001 From: Teodor Dutu Date: Tue, 11 Oct 2022 00:03:59 +0300 Subject: [PATCH] Translate `_d_arraycatnTX` to a template This brings the following changes: - Improves the existing template `_d_arraycatnTX`, to now concatenates both arrays and single elements - Changes the lowerings to `_d_arraycatT` to use the new template - Moves the lowering logic to `_d_arraycatnTX` to expressionsem.d - Adds a new field to `CatExp` called `lowering` to store the template lowering - Removes the old non-template `_d_arraycatnTX` and `_d_arraycatT` hooks - Moves `test19688.d` from `runnable/` to `compilable/` until https://issues.dlang.org/show_bug.cgi?id=23408 is fixed Signed-off-by: Teodor Dutu --- compiler/src/dmd/dinterpret.d | 61 ++++-- compiler/src/dmd/e2ir.d | 46 +---- compiler/src/dmd/expression.d | 4 +- compiler/src/dmd/expression.h | 2 + compiler/src/dmd/expressionsem.d | 105 ++++++++-- compiler/src/dmd/frontend.h | 3 + compiler/src/dmd/id.d | 2 + compiler/src/dmd/inline.d | 27 +++ .../test/{runnable => compilable}/test19688.d | 0 .../extra-files/hello-profile-postscript.sh | 4 +- compiler/test/unit/lexer/location_offset.d | 2 +- .../src/core/internal/array/concatenation.d | 187 ++++++++++++++---- druntime/src/object.d | 5 +- druntime/src/rt/lifetime.d | 142 ------------- druntime/src/rt/tracegc.d | 2 - 15 files changed, 324 insertions(+), 268 deletions(-) rename compiler/test/{runnable => compilable}/test19688.d (100%) diff --git a/compiler/src/dmd/dinterpret.d b/compiler/src/dmd/dinterpret.d index 255a8f2826e7..d1fabe852e3d 100644 --- a/compiler/src/dmd/dinterpret.d +++ b/compiler/src/dmd/dinterpret.d @@ -5750,7 +5750,25 @@ public: e2 = ue2.copy(); } - *pue = ctfeCat(e.loc, e.type, e1, e2); + Expression prepareCatOperand(Expression exp) + { + /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an + * array. This is needed because interpreting the `CatExp` calls + * `Cat()`, which cannot handle concatenations between different + * types, except for strings and chars. + */ + auto tb = e.type.toBasetype(); + auto tbNext = tb.nextOf(); + auto expTb = exp.type.toBasetype(); + + if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && + (tb.ty == Tarray || tb.ty == Tsarray) && + (expTb.ty == Tarray || expTb.ty == Tsarray)) + return new ArrayLiteralExp(exp.loc, e.type, exp); + return exp; + } + + *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2)); result = pue.exp(); if (CTFEExp.isCantExp(result)) @@ -6442,33 +6460,34 @@ void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, In } } - /********************************************* - * Checks if the given expresion is a call to the runtime hook `id`. - * Params: - * e = the expression to check - * id = the identifier of the runtime hook - * Returns: - * `e` cast to `CallExp` if it's the hook, `null` otherwise - */ - private CallExp isRuntimeHook(Expression e, Identifier id) +/********************************************* + * Checks if the given expresion is a call to the runtime hook `id`. + * + * Params: + * e = the expression to check + * id = the identifier of the runtime hook + * Returns: + * `e` cast to `CallExp` if it's the hook, `null` otherwise + */ +public CallExp isRuntimeHook(Expression e, Identifier id) +{ + if (auto ce = e.isCallExp()) { - if (auto ce = e.isCallExp()) + if (auto ve = ce.e1.isVarExp()) { - if (auto ve = ce.e1.isVarExp()) + if (auto fd = ve.var.isFuncDeclaration()) { - if (auto fd = ve.var.isFuncDeclaration()) - { - // If `_d_HookTraceImpl` is found, resolve the underlying - // hook and replace `e` and `fd` with it. - removeHookTraceImpl(ce, fd); - return fd.ident == id ? ce : null; - } + // If `_d_HookTraceImpl` is found, resolve the underlying hook + // and replace `e` and `fd` with it. + removeHookTraceImpl(ce, fd); + return fd.ident == id ? ce : null; } } - - return null; } + return null; +} + /******************************************** * Interpret the expression. * Params: diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d index 54a3b75deed0..ae3989affd26 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -1725,50 +1725,10 @@ elem* toElem(Expression e, IRState *irs) return el_long(TYint, 0); } - Type tb1 = ce.e1.type.toBasetype(); - Type tb2 = ce.e2.type.toBasetype(); - - Type ta = (tb1.ty == Tarray || tb1.ty == Tsarray) ? tb1 : tb2; - - elem *e; - if (ce.e1.op == EXP.concatenate) - { - CatExp ex = ce; - - // Flatten ((a ~ b) ~ c) to [a, b, c] - Elems elems; - elems.shift(array_toDarray(ex.e2.type, toElem(ex.e2, irs))); - do - { - ex = cast(CatExp)ex.e1; - elems.shift(array_toDarray(ex.e2.type, toElem(ex.e2, irs))); - } while (ex.e1.op == EXP.concatenate); - elems.shift(array_toDarray(ex.e1.type, toElem(ex.e1, irs))); + if (auto lowering = ce.lowering) + return toElem(lowering, irs); - // We can't use ExpressionsToStaticArray because each exp needs - // to have array_toDarray called on it first, as some might be - // single elements instead of arrays. - Symbol *sdata; - elem *earr = ElemsToStaticArray(ce.loc, ce.type, &elems, &sdata); - - elem *ep = el_pair(TYdarray, el_long(TYsize_t, elems.length), el_ptr(sdata)); - if (irs.target.os == Target.OS.Windows && irs.target.is64bit) - ep = addressElem(ep, Type.tvoid.arrayOf()); - ep = el_param(ep, getTypeInfo(ce, ta, irs)); - e = el_bin(OPcall, TYdarray, el_var(getRtlsym(RTLSYM.ARRAYCATNTX)), ep); - toTraceGC(irs, e, ce.loc); - e = el_combine(earr, e); - } - else - { - elem *e1 = eval_Darray(ce.e1); - elem *e2 = eval_Darray(ce.e2); - elem *ep = el_params(e2, e1, getTypeInfo(ce, ta, irs), null); - e = el_bin(OPcall, TYdarray, el_var(getRtlsym(RTLSYM.ARRAYCATT)), ep); - toTraceGC(irs, e, ce.loc); - } - elem_setLoc(e,ce.loc); - return e; + assert(0, "This case should have been rewritten to `_d_arraycatnTX` in the semantic phase"); } /*************************************** diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 4226709220ec..067d22fe1306 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -1542,7 +1542,7 @@ extern (C++) abstract class Expression : ASTNode // so don't print anything to avoid double error messages. if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_newclassT)) + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) { error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); @@ -6555,6 +6555,8 @@ extern (C++) final class MinExp : BinExp */ extern (C++) final class CatExp : BinExp { + Expression lowering; // call to druntime hook `_d_arraycatnTX` + extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope { super(loc, EXP.concatenate, e1, e2); diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index 1c38ec56fd44..a4b18b9fe9ef 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -1207,6 +1207,8 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression *lowering; // call to druntime hook `_d_arraycatnTX` + void accept(Visitor *v) override { v->visit(this); } }; diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 2b44f5359111..0b8e2137b8c4 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -2176,7 +2176,11 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. - else if (!(pStc & STC.lazy_)) + // The arguments of `_d_arraycatnTX` are already handled in + // expressionsem.d, via `checkNewEscape`. Without `-dip1000`, the + // check does not return an error, so the lowering of `a ~ b` to + // `_d_arraycatnTX(a, b)` still occurs. + else if (!(pStc & STC.lazy_) && (!fd || fd.ident != Id._d_arraycatnTX)) { /* Argument value can escape from the called function. * Check arg to see if it matters. @@ -2207,6 +2211,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, // allocate the array literal as temporary static array on the stack ale.type = ale.type.nextOf().sarrayOf(ale.elements.length); auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale); + tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type.substWildTo(MODFlags.mutable)); @@ -10930,6 +10935,86 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + /** + * If the given expression is a `CatExp`, the function tries to lower it to + * `_d_arraycatnTX`. + * + * Params: + * ee = the `CatExp` to lower + * Returns: + * `_d_arraycatnTX(e1, e2, ..., en)` if `ee` is `e1 ~ e2 ~ ... en` + * `ee` otherwise + */ + private Expression lowerToArrayCat(CatExp exp) + { + // String literals are concatenated by the compiler. No lowering is needed. + if ((exp.e1.isStringExp() && (exp.e2.isIntegerExp() || exp.e2.isStringExp())) || + (exp.e2.isStringExp() && (exp.e1.isIntegerExp() || exp.e1.isStringExp()))) + return exp; + + Identifier hook = global.params.tracegc ? Id._d_arraycatnTXTrace : Id._d_arraycatnTX; + if (!verifyHookExist(exp.loc, *sc, hook, "concatenating arrays")) + { + setError(); + return result; + } + + void handleCatArgument(Expressions *arguments, Expression e) + { + if (auto ce = e.isCatExp()) + { + Expression lowering = ce.lowering; + + /* Skip `file`, `line`, and `funcname` if the hook of the parent + * `CatExp` is `_d_arraycatnTXTrace`. + */ + if (auto callExp = isRuntimeHook(lowering, hook)) + { + if (hook == Id._d_arraycatnTX) + arguments.pushSlice((*callExp.arguments)[]); + else + arguments.pushSlice((*callExp.arguments)[3 .. $]); + } + } + else + arguments.push(e); + } + + auto arguments = new Expressions(); + if (global.params.tracegc) + { + auto funcname = (sc.callsc && sc.callsc.func) ? + sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); + arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); + arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); + arguments.push(new StringExp(exp.loc, funcname.toDString())); + } + + handleCatArgument(arguments, exp.e1); + handleCatArgument(arguments, exp.e2); + + Expression id = new IdentifierExp(exp.loc, Id.empty); + id = new DotIdExp(exp.loc, id, Id.object); + + auto tiargs = new Objects(); + tiargs.push(exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); + id = new CallExp(exp.loc, id, arguments); + return id.expressionSemantic(sc); + } + + void trySetCatExpLowering(Expression exp) + { + /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be + * used with `-betterC`, but only during CTFE. + */ + if (global.params.betterC) + return; + + if (auto ce = exp.isCatExp()) + ce.lowering = lowerToArrayCat(ce); + } + override void visit(CatExp exp) { // https://dlang.org/spec/expression.html#cat_expressions @@ -11017,14 +11102,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (tb2.ty == Tarray || tb2.ty == Tsarray) - { - // Make e2 into [e2] - exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2); - } - else if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11055,14 +11136,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (tb1.ty == Tarray || tb1.ty == Tsarray) - { - // Make e1 into [e1] - exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1); - } - else if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11085,6 +11162,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (Expression ex = typeCombine(exp, sc)) { result = ex; + trySetCatExpLowering(result); return; } exp.type = exp.type.toHeadMutable(); @@ -11117,6 +11195,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } result = e; + trySetCatExpLowering(result); } override void visit(MulExp exp) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 5be265dc13d7..806707f56eff 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -7872,6 +7872,7 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression lowering; Expression* resolveLoc(const Loc& loc, Scope* sc) override; void accept(Visitor* v) override; }; @@ -8731,6 +8732,8 @@ struct Id final static Identifier* _d_arrayappendcTXImpl; static Identifier* _d_arrayappendcTX; static Identifier* _d_arrayappendcTXTrace; + static Identifier* _d_arraycatnTX; + static Identifier* _d_arraycatnTXTrace; static Identifier* stdc; static Identifier* stdarg; static Identifier* va_start; diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index bffad0b7bd67..2c88b9fa9016 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -361,6 +361,8 @@ immutable Msgtable[] msgtable = { "_d_arrayappendcTXImpl" }, { "_d_arrayappendcTX" }, { "_d_arrayappendcTXTrace" }, + { "_d_arraycatnTX" }, + { "_d_arraycatnTXTrace" }, // varargs implementation { "stdc" }, diff --git a/compiler/src/dmd/inline.d b/compiler/src/dmd/inline.d index aaa7c3174c7d..dd13638b7da3 100644 --- a/compiler/src/dmd/inline.d +++ b/compiler/src/dmd/inline.d @@ -747,6 +747,21 @@ public: result = ae; } + override void visit(CatExp e) + { + auto ce = e.copy().isCatExp(); + + if (auto lowering = ce.lowering) + ce.lowering = doInlineAs!Expression(lowering, ids); + else + { + ce.e1 = doInlineAs!Expression(e.e1, ids); + ce.e2 = doInlineAs!Expression(e.e2, ids); + } + + result = ce; + } + override void visit(BinExp e) { auto be = cast(BinExp)e.copy(); @@ -1241,6 +1256,18 @@ public: inlineScan(e.msg); } + override void visit(CatExp e) + { + if (auto lowering = e.lowering) + { + inlineScan(lowering); + return; + } + + inlineScan(e.e1); + inlineScan(e.e2); + } + override void visit(BinExp e) { inlineScan(e.e1); diff --git a/compiler/test/runnable/test19688.d b/compiler/test/compilable/test19688.d similarity index 100% rename from compiler/test/runnable/test19688.d rename to compiler/test/compilable/test19688.d diff --git a/compiler/test/runnable/extra-files/hello-profile-postscript.sh b/compiler/test/runnable/extra-files/hello-profile-postscript.sh index 47c18c089559..cb986e0a4fe2 100755 --- a/compiler/test/runnable/extra-files/hello-profile-postscript.sh +++ b/compiler/test/runnable/extra-files/hello-profile-postscript.sh @@ -3,7 +3,9 @@ source tools/common_funcs.sh # strip out Dmain since it's symbol differs between windows and non-windows -grep -v Dmain ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 +# strip out _d_arraycatnTX and _d_arraysetlengthT since they are part of the +# lowering of the array concatenation operator +grep -v 'Dmain\|_d_arraycatnTX\|_d_arraysetlengthT' ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 diff -up --strip-trailing-cr ${EXTRA_FILES}/${TEST_NAME}.d.trace.def ${OUTPUT_BASE}.d.trace.def2 diff --git a/compiler/test/unit/lexer/location_offset.d b/compiler/test/unit/lexer/location_offset.d index 873d98123821..4a950e866f26 100644 --- a/compiler/test/unit/lexer/location_offset.d +++ b/compiler/test/unit/lexer/location_offset.d @@ -557,7 +557,7 @@ static foreach (tok; __traits(allMembers, TOK)) @(tests[tok].description) unittest { - const newCode = "first_token " ~ tests[tok].code; + const newCode = "first_token " ~ tests[tok].code ~ '\0'; scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr, null); diff --git a/druntime/src/core/internal/array/concatenation.d b/druntime/src/core/internal/array/concatenation.d index 99f33da7683d..ff777a6b3ab6 100644 --- a/druntime/src/core/internal/array/concatenation.d +++ b/druntime/src/core/internal/array/concatenation.d @@ -8,71 +8,172 @@ */ module core.internal.array.concatenation; -/// See $(REF _d_arraycatnTX, rt,lifetime) -private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow; - -/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace` -template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) +/** + * Concatenate the arrays inside of `froms`. + * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`. + * + * Params: + * froms = Arrays to be concatenated. + * Returns: + * A newly allocated array that contains all the elements from `froms`. + */ +Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { - private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!"; + import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.lifetime : copyEmplace; + import core.stdc.string : memcpy; - /** - * Concatenating the arrays inside of `arrs`. - * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`. - * Params: - * arrs = Array containing arrays that will be concatenated. - * Returns: - * A newly allocated array that contains all the elements from all the arrays in `arrs`. - * 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. - */ - ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow + Tret res; + size_t totalLen; + + alias T = typeof(res[0]); + enum elemSize = T.sizeof; + enum hasPostblit = __traits(hasPostblit, T); + + static foreach (from; froms) + static if (is (typeof(from) : T)) + totalLen++; + else + totalLen += from.length; + + if (totalLen == 0) + return res; + res.length = totalLen; + + /* Currently, if both a postblit and a cpctor are defined, the postblit is + * used. If this changes, the condition below will have to be adapted. + */ + static if (hasElaborateCopyConstructor!T && !hasPostblit) { - pragma(inline, false); - version (D_TypeInfo) - { - auto ti = typeid(ResultArrT); + size_t i = 0; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + copyEmplace(cast(T) from, res[i++]); + else + { + if (from.length) + foreach (ref elem; from) + copyEmplace(cast(T) elem, res[i++]); + } + } + else + { + auto resptr = cast(Unqual!T *) res; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + memcpy(resptr++, cast(Unqual!T *) &from, elemSize); + else + { + const len = from.length; + if (len) + { + memcpy(resptr, cast(Unqual!T *) from, len * elemSize); + resptr += len; + } + } + + static if (hasPostblit) + foreach (ref elem; res) + (cast() elem).__xpostblit(); + } + + return res; +} - byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length]; - void[] result = ._d_arraycatnTX(ti, arrs2); - return (cast(T*)result.ptr)[0 .. result.length]; +// postblit +@safe unittest +{ + int counter; + struct S + { + int val; + this(this) + { + counter++; } - else - assert(0, errorMessage); } - version (D_ProfileGC) + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = []; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem); + + assert(counter == 7); + assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S { - import core.internal.array.utils : _d_HookTraceImpl; - - /** - * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat). - * 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. - */ - alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage); + int val; + this(ref return scope S rhs) + { + val = rhs.val; + counter++; + } } + + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3); + + assert(counter == 10); + assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]); } +// throwing @safe unittest { int counter; + bool didThrow; struct S { int val; this(this) { counter++; + if (counter == 4) + throw new Exception(""); } } - S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]]; - S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr); + try + { + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + _d_arraycatnTX!(S[])(arr1, arr2); + } + catch (Exception) + { + didThrow = true; + } + + assert(counter == 4); + assert(didThrow); +} + +version (D_ProfileGC) +{ + /** + * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation). + */ + Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted + { + version (D_TypeInfo) + { + import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure; + mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX")); - assert(counter == 8); - assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]); + import core.lifetime: forward; + return _d_arraycatnTX!Tret(forward!froms); + } + else + assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); + } } diff --git a/druntime/src/object.d b/druntime/src/object.d index a77788bec20d..610cb7a30a1f 100644 --- a/druntime/src/object.d +++ b/druntime/src/object.d @@ -4529,12 +4529,15 @@ public import core.internal.entrypoint : _d_cmain; public import core.internal.array.appending : _d_arrayappendT; version (D_ProfileGC) +{ public import core.internal.array.appending : _d_arrayappendTTrace; + public import core.internal.array.concatenation : _d_arraycatnTXTrace; +} public import core.internal.array.appending : _d_arrayappendcTXImpl; public import core.internal.array.comparison : __cmp; public import core.internal.array.equality : __equals; public import core.internal.array.casting: __ArrayCast; -public import core.internal.array.concatenation : _d_arraycatnTXImpl; +public import core.internal.array.concatenation : _d_arraycatnTX; public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.arrayassign : _d_arrayassign_l; diff --git a/druntime/src/rt/lifetime.d b/druntime/src/rt/lifetime.d index c5ca8f46973a..a37541bf01fe 100644 --- a/druntime/src/rt/lifetime.d +++ b/druntime/src/rt/lifetime.d @@ -2231,148 +2231,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak return x; } - -/** -Concatenate two arrays into a new array - ---- -void main() -{ - int[] x = [10, 20, 30]; - int[] y = [40, 50]; - int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); -} ---- - -Params: - ti = type that the two arrays share - x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length - y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length -Returns: - resulting concatenated array, with `.length` equal to new element length despite `byte` type -*/ -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak -out (result) -{ - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr); - assert(result.length == x.length + y.length); - - // If a postblit is involved, the contents of result might rightly differ - // from the bitwise concatenation of x and y. - if (!hasPostblit(tinext)) - { - for (size_t i = 0; i < x.length * sizeelem; i++) - assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); - for (size_t i = 0; i < y.length * sizeelem; i++) - assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); - } - - size_t cap = GC.sizeOf(result.ptr); - assert(!cap || cap > result.length * sizeelem); -} -do -{ - import core.stdc.string; - version (none) - { - /* Cannot use this optimization because: - * char[] a, b; - * char c = 'a'; - * b = a ~ c; - * c = 'b'; - * will change the contents of b. - */ - if (!y.length) - return x; - if (!x.length) - return y; - } - - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); - size_t xlen = x.length * sizeelem; - size_t ylen = y.length * sizeelem; - size_t len = xlen + ylen; - - if (!len) - return null; - - auto info = __arrayAlloc(len, ti, tinext); - byte* p = cast(byte*)__arrayStart(info); - p[len] = 0; // guessing this is to optimize for null-terminated arrays? - memcpy(p, x.ptr, xlen); - memcpy(p + xlen, y.ptr, ylen); - // do postblit processing - __doPostblit(p, xlen + ylen, tinext); - - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, len, isshared, tinext); - return p[0 .. x.length + y.length]; -} - - -/** -Concatenate multiple arrays at once - -This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. - -``` -void main() -{ - int[] a, b, c; - int[] res = a ~ b ~ c; - // _d_arraycatnTX(typeid(int[]), - // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); -} -``` - -Params: - ti = type of arrays to concatenate and resulting array - arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same - -Returns: - newly created concatenated array, `.length` equal to the total element length despite `void` type -*/ -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak -{ - import core.stdc.string; - - size_t length; - auto tinext = unqualify(ti.next); - auto size = tinext.tsize; // array element size - - foreach (b; arrs) - length += b.length; - - if (!length) - return null; - - auto allocsize = length * size; - auto info = __arrayAlloc(allocsize, ti, tinext); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, allocsize, isshared, tinext); - void *a = __arrayStart (info); - - size_t j = 0; - foreach (b; arrs) - { - if (b.length) - { - memcpy(a + j, b.ptr, b.length * size); - j += b.length * size; - } - } - - // do postblit processing - __doPostblit(a, j, tinext); - - return a[0..length]; -} - - /** Allocate an array literal diff --git a/druntime/src/rt/tracegc.d b/druntime/src/rt/tracegc.d index c89a358742ca..ab65cb9887a8 100644 --- a/druntime/src/rt/tracegc.d +++ b/druntime/src/rt/tracegc.d @@ -29,8 +29,6 @@ extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); extern (C) void _d_delinterface(void** p); extern (C) void _d_delmemory(void* *p); -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs); extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals);