Skip to content

Commit 4237d3b

Browse files
authoredNov 1, 2020
Merge pull request #11828 from kinke/preview_in
Revise ABI-specific part of -preview=in merged-on-behalf-of: Mathias LANG <pro.mathias.lang@gmail.com>
2 parents b35430e + 7ae93c9 commit 4237d3b

File tree

10 files changed

+155
-133
lines changed

10 files changed

+155
-133
lines changed
 

‎src/dmd/frontend.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -6541,7 +6541,7 @@ struct Target
65416541
TypeTuple* toArgTypes(Type* t);
65426542
bool isReturnOnStack(TypeFunction* tf, bool needsThis);
65436543
uint64_t parameterSize(const Loc& loc, Type* t);
6544-
void applyInRefParams(TypeFunction* tf);
6544+
bool preferPassByRef(Type* t);
65456545
private:
65466546
enum class TargetInfoKeys
65476547
{

‎src/dmd/mtype.d

+4-4
Original file line numberDiff line numberDiff line change
@@ -6951,8 +6951,8 @@ extern (C++) final class Parameter : ASTNode
69516951
* Params:
69526952
* returnByRef = true if the function returns by ref
69536953
* p = Parameter to compare with
6954-
* previewIn = Whether `-previewIn` is being used, and thus if
6955-
* `in` means `scope`.
6954+
* previewIn = Whether `-preview=in` is being used, and thus if
6955+
* `in` means `scope [ref]`.
69566956
*
69576957
* Returns:
69586958
* true = `this` can be used in place of `p`
@@ -6972,8 +6972,8 @@ extern (C++) final class Parameter : ASTNode
69726972
otherSTC |= STC.scope_;
69736973
}
69746974

6975-
enum stc = STC.ref_ | STC.out_ | STC.lazy_;
6976-
if ((thisSTC & stc) != (otherSTC & stc))
6975+
const mask = STC.ref_ | STC.out_ | STC.lazy_ | (previewIn ? STC.in_ : 0);
6976+
if ((thisSTC & mask) != (otherSTC & mask))
69776977
return false;
69786978
return isCovariantScope(returnByRef, thisSTC, otherSTC);
69796979
}

‎src/dmd/mtype.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ class Parameter : public ASTNode
558558
static size_t dim(Parameters *parameters);
559559
static Parameter *getNth(Parameters *parameters, d_size_t nth);
560560
const char *toChars() const;
561-
bool isCovariant(bool returnByRef, const Parameter *p) const;
561+
bool isCovariant(bool returnByRef, const Parameter *p, bool previewIn) const;
562562
};
563563

564564
struct ParameterList

‎src/dmd/target.d

+48-47
Original file line numberDiff line numberDiff line change
@@ -776,60 +776,61 @@ extern (C++) struct Target
776776
}
777777

778778
/**
779-
* Determine which `in` parameters needs to be passed by `ref`
780-
*
781-
* Called from `TypeFunction` semantic with the full function type.
782-
* This routine must iterate over parameters, and may set `STC.ref_`
783-
* for any parameter which already have `STC.in_`.
784-
* This hook must never set `STC.ref_` if the parameter is not `STC.in_`,
785-
* nor should it ever change anything else.
786-
*
787-
* This hook will not be called when `-preview=in` wasn't passed to the
788-
* frontend, hence it needs not care about `params.previewIn`.
789-
*
779+
* Decides whether an `in` parameter of the specified POD type is to be
780+
* passed by reference or by value. To be used with `-preview=in` only!
790781
* Params:
791-
* tf = Type of the function to inspect. The type will have its
792-
* parameter types semantically resolved, however other attributes
793-
* (return type, `@safe` / `nothrow`, etc...) must not be used.
782+
* t = type of the `in` parameter, must be a POD
783+
* Returns:
784+
* `true` if the `in` parameter is to be passed by reference
794785
*/
795-
extern(C++) void applyInRefParams (TypeFunction tf)
786+
extern (C++) bool preferPassByRef(Type t)
796787
{
797-
foreach (_idx, p; tf.parameterList)
788+
const size = t.size();
789+
if (global.params.is64bit)
798790
{
799-
// Ignore non-`in` or already-`ref` parameters
800-
if ((p.storageClass & (STC.in_ | STC.ref_)) != STC.in_)
801-
continue;
802-
803-
assert(p.type !is null);
804-
// If it has a copy constructor / destructor / postblit,
805-
// it is always by ref
806-
if (p.type.needsDestruction() || p.type.needsCopyOrPostblit())
807-
p.storageClass |= STC.ref_;
808-
// If the type can't be copied, always by `ref`
809-
else if (!p.type.isCopyable())
810-
p.storageClass |= STC.ref_;
811-
// The Win64 ABI requires x87 real to be passed by ref
812-
else if (params.isWindows && params.is64bit &&
813-
p.type.ty == Tfloat80)
814-
p.storageClass |= STC.ref_;
815-
816-
// If it's a dynamic array, use the value type as it
817-
// allows covariance between `in char[]` and `scope const(char)[]`
818-
// The same reasoning applies to pointers and classes,
819-
// but that is handled by the `(sz > 8)` below.
820-
else if (p.type.ty == Tarray)
821-
continue;
822-
// Pass delegates by value to allow covariance
823-
// Function pointers are a single pointers and handled below.
824-
else if (p.type.ty == Tdelegate)
825-
continue;
826-
else
791+
if (global.params.isWindows)
792+
{
793+
// Win64 special case: by-value for slices and delegates due to
794+
// high number of usages in druntime/Phobos (compiled without
795+
// -preview=in but supposed to link against -preview=in code)
796+
const ty = t.toBasetype().ty;
797+
if (ty == Tarray || ty == Tdelegate)
798+
return false;
799+
800+
// If size is larger than 8 or not a power-of-2, the Win64 ABI
801+
// would require a hidden reference anyway.
802+
return size > 8
803+
|| (size > 0 && (size & (size - 1)) != 0);
804+
}
805+
else // SysV x86_64 ABI
827806
{
828-
const sz = p.type.size();
829-
if (params.is64bit ? (sz > 16) : (sz > 8))
830-
p.storageClass |= STC.ref_;
807+
// Prefer a ref if the POD cannot be passed in registers, i.e.,
808+
// would be passed on the stack, *and* the size is > 16.
809+
if (size <= 16)
810+
return false;
811+
812+
TypeTuple getArgTypes()
813+
{
814+
import dmd.aggregate : Sizeok;
815+
if (auto ts = t.toBasetype().isTypeStruct())
816+
{
817+
auto sd = ts.sym;
818+
assert(sd.sizeok == Sizeok.done);
819+
return sd.argTypes;
820+
}
821+
return toArgTypes(t);
822+
}
823+
824+
TypeTuple argTypes = getArgTypes();
825+
assert(argTypes !is null, "size == 0 should already be handled");
826+
return argTypes.arguments.length == 0; // cannot be passed in registers
831827
}
832828
}
829+
else // 32-bit x86 ABI
830+
{
831+
// Prefer a ref if the size is > 2 machine words.
832+
return size > 8;
833+
}
833834
}
834835

835836
// this guarantees `getTargetInfo` and `allTargetInfos` remain in sync

‎src/dmd/target.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct Target
109109
TypeTuple *toArgTypes(Type *t);
110110
bool isReturnOnStack(TypeFunction *tf, bool needsThis);
111111
d_uns64 parameterSize(const Loc& loc, Type *t);
112-
void applyInRefParams(TypeFunction *tf);
112+
bool preferPassByRef(Type *t);
113113
Expression *getTargetInfo(const char* name, const Loc& loc);
114114
};
115115

‎src/dmd/typesem.d

+9-5
Original file line numberDiff line numberDiff line change
@@ -1491,12 +1491,16 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
14911491

14921492
// Remove redundant storage classes for type, they are already applied
14931493
fparam.storageClass &= ~(STC.TYPECTOR);
1494-
}
14951494

1496-
// Now that we're done processing the types of parameters,
1497-
// apply `STC.ref` where necessary
1498-
if (global.params.previewIn)
1499-
target.applyInRefParams(tf);
1495+
// -preview=in: add `ref` storage class to suited `in` params
1496+
if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_)
1497+
{
1498+
auto ts = t.baseElemOf().isTypeStruct();
1499+
const isPOD = !ts || ts.sym.isPOD();
1500+
if (!isPOD || target.preferPassByRef(t))
1501+
fparam.storageClass |= STC.ref_;
1502+
}
1503+
}
15001504

15011505
// Now that we completed semantic for the argument types,
15021506
// run semantic on their default values,

‎test/compilable/previewin.d

+49-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* REQUIRED_ARGS: -preview=dip1000 -preview=in
1+
/* REQUIRED_ARGS: -preview=dip1000 -preview=in -mcpu=native
22
*/
33

44
import core.stdc.time;
@@ -24,14 +24,13 @@ struct FooBar
2424
string toString() const
2525
{
2626
string result;
27+
// Type is const
28+
this.toString((in char[] buf) {
29+
static assert(is(typeof(buf) == const(char[])));
30+
result ~= buf;
31+
});
2732
// Type inference works
28-
this.toString((buf) { result ~= buf; });
29-
// Specifying the STC too
3033
this.toString((in buf) { result ~= buf; });
31-
// Some covariance
32-
this.toString((const scope buf) { result ~= buf; });
33-
this.toString((scope const(char)[] buf) { result ~= buf; });
34-
this.toString((scope const(char[]) buf) { result ~= buf; });
3534
return result;
3635
}
3736

@@ -64,3 +63,46 @@ void checkTemporary()
6463
LError:
6564
assert(0);
6665
}
66+
67+
68+
// Some ABI-specific tests:
69+
70+
version (Win64)
71+
{
72+
void checkReal(in real p)
73+
{
74+
// ref for x87 real, value for double-precision real
75+
static assert(__traits(isRef, p) == (real.sizeof > 8));
76+
}
77+
78+
struct RGB { ubyte r, g, b; }
79+
void checkNonPowerOf2(in RGB p)
80+
{
81+
static assert(__traits(isRef, p));
82+
}
83+
}
84+
else version (X86_64) // Posix x86_64
85+
{
86+
struct Empty {} // 1 dummy byte passed on the stack
87+
void checkEmptyStruct(in Empty p)
88+
{
89+
static assert(!__traits(isRef, p));
90+
}
91+
92+
static if (is(__vector(double[4])))
93+
{
94+
struct AvxVectorWrapper { __vector(double[4]) a; } // 256 bits
95+
void checkAvxVector(in AvxVectorWrapper p)
96+
{
97+
static assert(!__traits(isRef, p));
98+
}
99+
}
100+
}
101+
else version (AArch64)
102+
{
103+
alias HVA = __vector(float[4])[4]; // can be passed in 4 vector registers
104+
void checkHVA(in HVA p)
105+
{
106+
static assert(!__traits(isRef, p));
107+
}
108+
}

‎test/fail_compilation/diagin.d

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
PERMUTE_ARGS: -preview=in
33
TEST_OUTPUT:
44
---
5-
fail_compilation/diagin.d(14): Error: function `diagin.foo(in string)` is not callable using argument types `()`
6-
fail_compilation/diagin.d(14): missing argument for parameter #1: `in string`
5+
fail_compilation/diagin.d(14): Error: function `diagin.foo(in int)` is not callable using argument types `()`
6+
fail_compilation/diagin.d(14): missing argument for parameter #1: `in int`
77
fail_compilation/diagin.d(16): Error: template `diagin.foo1` cannot deduce function from argument types `!()(bool[])`, candidates are:
88
fail_compilation/diagin.d(20): `foo1(T)(in T v, string)`
99
---
@@ -16,10 +16,10 @@ void main ()
1616
foo1(lvalue);
1717
}
1818

19-
void foo(in string) {}
19+
void foo(in int) {}
2020
void foo1(T)(in T v, string) {}
2121

2222
// Ensure that `in` has a unique mangling
23-
static assert(foo.mangleof == `_D6diagin3fooFIAyaZv`);
23+
static assert(foo.mangleof == `_D6diagin3fooFIiZv`);
2424
static assert(foo1!int.mangleof == `_D6diagin__T4foo1TiZQiFNaNbNiNfIiAyaZv`);
2525
static assert(foo1!char.mangleof == `_D6diagin__T4foo1TaZQiFNaNbNiNfIaAyaZv`);

‎test/fail_compilation/previewin.d

+17-43
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,33 @@
22
REQUIRED_ARGS: -preview=in -preview=dip1000
33
TEST_OUTPUT:
44
---
5-
fail_compilation/previewin.d(3): Error: function `previewin.func1(void function(ulong[8]) dg)` is not callable using argument types `(void function(in ulong[8]))`
6-
fail_compilation/previewin.d(3): cannot pass argument `& func_byRef` of type `void function(in ulong[8])` to parameter `void function(ulong[8]) dg`
7-
fail_compilation/previewin.d(4): Error: function `previewin.func2(void function(ref ulong[8]) dg)` is not callable using argument types `(void function(in ulong[8]))`
8-
fail_compilation/previewin.d(4): cannot pass argument `& func_byRef` of type `void function(in ulong[8])` to parameter `void function(ref ulong[8]) dg`
9-
fail_compilation/previewin.d(7): Error: function `previewin.func4(void function(ref uint) dg)` is not callable using argument types `(void function(in uint))`
10-
fail_compilation/previewin.d(7): cannot pass argument `& func_byValue` of type `void function(in uint)` to parameter `void function(ref uint) dg`
11-
fail_compilation/previewin.d(41): Error: scope variable `arg` assigned to non-scope `myGlobal`
12-
fail_compilation/previewin.d(42): Error: scope variable `arg` assigned to non-scope `myGlobal`
13-
fail_compilation/previewin.d(43): Error: scope variable `arg` may not be returned
14-
fail_compilation/previewin.d(44): Error: scope variable `arg` assigned to `escape` with longer lifetime
15-
fail_compilation/previewin.d(48): Error: returning `arg` escapes a reference to parameter `arg`
16-
fail_compilation/previewin.d(48): perhaps annotate the parameter with `return`
5+
fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)`
6+
fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
7+
fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)`
8+
fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
9+
fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)`
10+
fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
11+
fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to non-scope `myGlobal`
12+
fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to non-scope `myGlobal`
13+
fail_compilation/previewin.d(17): Error: scope variable `arg` may not be returned
14+
fail_compilation/previewin.d(18): Error: scope variable `arg` assigned to `escape` with longer lifetime
15+
fail_compilation/previewin.d(22): Error: returning `arg` escapes a reference to parameter `arg`
16+
fail_compilation/previewin.d(22): perhaps annotate the parameter with `return`
1717
---
1818
*/
1919

2020
#line 1
2121
void main ()
2222
{
23-
func1(&func_byRef); // No
24-
func2(&func_byRef); // No
25-
func3(&func_byRef); // Could be Yes, but currently No
26-
27-
func4(&func_byValue); // No
28-
func5(&func_byValue); // Yes
29-
30-
func6(&func_byValue2); // Yes
31-
func7(&func_byValue3); // Yes
23+
// No covariance without explicit `in`
24+
takeFunction((real x) {});
25+
takeFunction((const scope real x) {});
26+
takeFunction((const scope ref real x) {});
3227

3328
tryEscape("Hello World"); // Yes by `tryEscape` is NG
3429
}
3530

36-
// Takes by `scope ref const`
37-
void func_byRef(in ulong[8]) {}
38-
// Takes by `scope const`
39-
void func_byValue(in uint) {}
40-
41-
// Error: `ulong[8]` is passed by `ref`
42-
void func1(void function(scope ulong[8]) dg) {}
43-
// Error: Missing `scope` on a `ref`
44-
void func2(void function(ref ulong[8]) dg) {}
45-
// Works: `scope ref`
46-
void func3(void function(scope const ref ulong[8]) dg) {}
47-
48-
// Error: `uint` is passed by value
49-
void func4(void function(ref uint) dg) {}
50-
// Works: By value `scope const`
51-
void func5(void function(scope const uint) dg) {}
52-
53-
// This works for arrays:
54-
void func_byValue2(in char[]) {}
55-
void func6(void function(char[]) dg) {}
56-
void func_byValue3(scope const(char)[]) {}
57-
void func7(void function(in char[]) dg) {}
31+
void takeFunction(void function(in real) f);
5832

5933
// Make sure things cannot be escaped (`scope` is applied)
6034
const(char)[] myGlobal;

‎test/runnable/previewin.d

+21-20
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ void testWithAllAttributes() @safe pure nothrow @nogc
1313

1414
// rvalues
1515
testin1(42);
16-
testin2([42, ulong.max, ulong.min, 42UL]);
16+
testin2((ulong[64]).init);
1717
testin3(ValueT(42));
1818
testin3(RefT(42));
19-
testin4([ValueT(0), ValueT(1), ValueT(2), ValueT(3),
20-
ValueT(4), ValueT(5), ValueT(6), ValueT(7)]);
19+
testin4((ValueT[64]).init);
2120
testin4([RefT(42), RefT(84), RefT(126), RefT(4)]);
2221
testin5(NonCopyable(true));
2322
testin6(WithPostblit(true));
@@ -27,14 +26,14 @@ void testWithAllAttributes() @safe pure nothrow @nogc
2726
isTestOver = false;
2827

2928
// lvalues
30-
uint a1;
31-
ulong[4] a2;
32-
ValueT a3;
33-
ValueT[8] a4;
34-
RefT a5;
35-
RefT[4] a6;
29+
uint a1;
30+
ulong[64] a2;
31+
ValueT a3;
32+
ValueT[64] a4;
33+
RefT a5;
34+
RefT[4] a6;
3635
NonCopyable a7 = NonCopyable(true);
37-
WithPostblit a8;
36+
WithPostblit a8;
3837
WithCopyCtor a9;
3938
WithDtor a10 = WithDtor(&isTestOver);
4039

@@ -118,7 +117,7 @@ void testForeach() @safe pure
118117
}
119118

120119
struct ValueT { int value; }
121-
struct RefT { ubyte[64] value; }
120+
struct RefT { ulong[64] value; }
122121

123122
struct NonCopyable
124123
{
@@ -155,25 +154,27 @@ struct WithDtor
155154
@safe pure nothrow @nogc:
156155

157156
// By value
158-
void testin1(in uint p) {}
157+
void testin1(in uint p) { static assert(!__traits(isRef, p)); }
159158
// By ref because of size
160-
void testin2(in ulong[4] p) {}
159+
void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); }
161160
// By value or ref depending on size
162-
void testin3(in ValueT p) {}
163-
void testin3(in RefT p) {}
161+
void testin3(in ValueT p) { static assert(!__traits(isRef, p)); }
162+
void testin3(in RefT p) { static assert(__traits(isRef, p)); }
164163
// By ref because of size
165-
void testin4(in ValueT[8] p) {}
166-
void testin4(in RefT[4] p) {}
164+
void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); }
165+
void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }
167166

168167
// By ref because of non-copyability
169-
void testin5(in NonCopyable noncopy) {}
168+
void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); }
169+
static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref`
170170
// By ref because of postblit
171-
void testin6(in WithPostblit withposblit) {}
171+
void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); }
172172
// By ref because of copy ctor
173-
void testin7(in WithCopyCtor withcopy) {}
173+
void testin7(in WithCopyCtor withcopy) { static assert(__traits(isRef, withcopy)); }
174174
// By ref because of dtor
175175
void testin8(in WithDtor withdtor, scope bool* isTestOver)
176176
{
177+
static assert(__traits(isRef, withdtor));
177178
if (isTestOver)
178179
*isTestOver = true;
179180
}

0 commit comments

Comments
 (0)
Please sign in to comment.