Skip to content

Commit 601bef5

Browse files
faresbakhitdkorpel
andauthored
Fix dlang/dmd!21024 - Optimize x^^c expressions (dlang/dmd!21082)
* Fix dlang/dmd!21024 - Optimize x^^c expressions Reason for the *magic* constraint c<8 on inlining x^^c: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/fpu/e_powl.S;h=47f129f34d368d7c67b8e5f2462b36b0bebb7621;hb=HEAD#l136 * Fix poor assumption about expression state * Restrict optimization to floating point expressions * Generalize optimization to any scalar data type * Fix segfault on x^^c where x is a single member anonymous enum DMD segfaulted on compiling the unittests in std/algorithm/sorting.o, the unittest that caused the segfault can be reduced to: enum real Two = 2.0; auto _ = Two^^3; I'm not sure why copying the anonymous enum into a `const` variable causes the compiler to segfault. * Add tests to x^^c inlining optimization * Fix missing type for e1 ^^ -1 to 1 / e1 rewrite * Move rewrites from constant folding to expression semantic and restrict them to [-1, 2] * Improve error message for the x^^2 rewrite. Before: ex.d(4): Error: can implicitly convert expression `(const const(double) __powtmp2 = x + 5.0;) , __powtmp2 * ...` of type `double` to `int` int y = ( x + 5 ) ^^ 2; ^ and after: ex.d(4): Error: cannot implicitly convert expression `(x + 5.0) ^^ 2L` of type `double` to `int` int y = ( x + 5 ) ^^ 2; ^ * Update C++ frontend header to match change in `CommaExp` * Address code review feedback Co-authored-by: Dennis Korpel <[email protected]> --------- Co-authored-by: Dennis Korpel <[email protected]>
1 parent f4d3d4f commit 601bef5

File tree

7 files changed

+151
-0
lines changed

7 files changed

+151
-0
lines changed

dmd/expression.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3851,13 +3851,22 @@ extern (C++) final class CommaExp : BinExp
38513851
/// false will be passed will be from the parser.
38523852
bool allowCommaExp;
38533853

3854+
/// The original expression before any rewriting occurs.
3855+
/// This is used in error messages.
3856+
Expression originalExp;
38543857

38553858
extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe
38563859
{
38573860
super(loc, EXP.comma, e1, e2);
38583861
allowCommaExp = isGenerated = generated;
38593862
}
38603863

3864+
extern (D) this(Loc loc, Expression e1, Expression e2, Expression oe) @safe
3865+
{
3866+
this(loc, e1, e2);
3867+
originalExp = oe;
3868+
}
3869+
38613870
override bool isLvalue()
38623871
{
38633872
return !rvalue && e2.isLvalue();

dmd/expression.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,7 @@ class CommaExp final : public BinExp
990990
public:
991991
d_bool isGenerated;
992992
d_bool allowCommaExp;
993+
Expression* originalExp;
993994
bool isLvalue() override;
994995
Optional<bool> toBool() override;
995996
void accept(Visitor *v) override { v->visit(this); }

dmd/expressionsem.d

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12796,6 +12796,58 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
1279612796
return;
1279712797
}
1279812798

12799+
// Inline the expression, if possible.
12800+
PowExp pe = cast(PowExp)e;
12801+
if (pe.e1.type.isScalar() && pe.e2.isIntegerExp())
12802+
{
12803+
Expression one;
12804+
if (pe.e1.type.isIntegral()) {
12805+
one = new IntegerExp(e.loc, 1, pe.e1.type);
12806+
} else {
12807+
one = new RealExp(e.loc, CTFloat.one, pe.e1.type);
12808+
}
12809+
12810+
const expo = cast(sinteger_t)pe.e2.toInteger();
12811+
// Replace e1 ^^ -1 with 1 / e1
12812+
if (expo == -1)
12813+
{
12814+
Expression ex = new DivExp(exp.loc, one, pe.e1);
12815+
ex = ex.expressionSemantic(sc);
12816+
result = ex;
12817+
return;
12818+
}
12819+
// Replace e1 ^^ 0 with 1
12820+
else if (expo == 0)
12821+
{
12822+
Expression ex = one;
12823+
ex.loc = exp.loc;
12824+
ex = ex.expressionSemantic(sc);
12825+
result = ex;
12826+
return;
12827+
}
12828+
// Replace e1 ^^ 1 with e1
12829+
else if (expo == 1)
12830+
{
12831+
Expression ex = pe.e1;
12832+
ex.loc = exp.loc;
12833+
ex = ex.expressionSemantic(sc);
12834+
result = ex;
12835+
return;
12836+
}
12837+
// Replace e1 ^^ 2 with e1 * e1
12838+
else if (expo == 2)
12839+
{
12840+
auto v = copyToTemp(STC.const_, "__powtmp", pe.e1);
12841+
auto ve = new VarExp(exp.loc, v);
12842+
auto de = new DeclarationExp(exp.e1.loc, v);
12843+
auto me = new MulExp(exp.e2.loc, ve, ve);
12844+
Expression ex = new CommaExp(exp.loc, de, me, exp);
12845+
ex = ex.expressionSemantic(sc);
12846+
result = ex;
12847+
return;
12848+
}
12849+
}
12850+
1279912851
Module mmath = Module.loadStdMath();
1280012852
if (!mmath)
1280112853
{

dmd/frontend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,7 @@ class CommaExp final : public BinExp
27662766
public:
27672767
const bool isGenerated;
27682768
bool allowCommaExp;
2769+
Expression* originalExp;
27692770
bool isLvalue() override;
27702771
Optional<bool > toBool() override;
27712772
void accept(Visitor* v) override;

dmd/hdrgen.d

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,6 +2724,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
27242724

27252725
void visitComma(CommaExp e)
27262726
{
2727+
if (e.originalExp !is null)
2728+
{
2729+
e.originalExp.expressionPrettyPrint(buf, hgs);
2730+
return;
2731+
}
2732+
27272733
// CommaExp is generated by the compiler so it shouldn't
27282734
// appear in error messages or header files.
27292735
// For now, this treats the case where the compiler
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/powinline.d(25): Error: cannot implicitly convert expression `(a + 5.0) ^^ 2L` of type `double` to `int`
5+
fail_compilation/powinline.d(26): Error: cannot implicitly convert expression `(1.0 / foo()) ^^ 2L` of type `double` to `int`
6+
fail_compilation/powinline.d(31): Error: void has no value
7+
fail_compilation/powinline.d(31): Error: incompatible types for `(5.0) * (bar())`: `double` and `void`
8+
fail_compilation/powinline.d(37): Error: cannot modify `immutable` expression `a`
9+
---
10+
*/
11+
12+
double foo()
13+
{
14+
return 5.0;
15+
}
16+
17+
void bar()
18+
{
19+
return;
20+
}
21+
22+
void test1()
23+
{
24+
double a = 2.0;
25+
int b = (a + 5.0) ^^ 2.0;
26+
b = (1 / foo()) ^^ 2.0;
27+
}
28+
29+
void test2()
30+
{
31+
double a = (5.0 * bar()) ^^ 2.0;
32+
}
33+
34+
void test3()
35+
{
36+
immutable double a = 3.0;
37+
(a ^^= 2.0) = 6;
38+
}

tests/dmd/runnable/powinline.d

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
REQUIRED_ARGS: -betterC
3+
RUN_OUTPUT:
4+
---
5+
Success
6+
---
7+
*/
8+
import core.stdc.stdio;
9+
10+
void test1()
11+
{
12+
enum real Two = 2.0;
13+
static assert(Two^^3 == 8.0);
14+
}
15+
16+
void test2()
17+
{
18+
double x = 5.0;
19+
assert(x^^-1 == 1/x);
20+
x = -1.0;
21+
assert(x^^1 == x);
22+
assert((x += 3) ^^ 2.0 == 4.0);
23+
assert((x) ^^ 2.0 == 4.0);
24+
assert((x *= 5) ^^ 2.0 == (x * x));
25+
assert(x^^-1 == 1.0 / x);
26+
assert((x^^-1) ^^ 0.0 == 1.0);
27+
}
28+
29+
void test3()
30+
{
31+
int x = 6;
32+
assert(x ^^ 0 == 1);
33+
assert((x += 3) ^^ 2 == 81);
34+
assert(x ^^ 2 == (x ^^ 1) * (x ^^ 1));
35+
static assert(4.0 ^^ -1 == 0.25);
36+
}
37+
38+
extern(C) void main()
39+
{
40+
test1();
41+
test2();
42+
test3();
43+
printf("Success\n");
44+
}

0 commit comments

Comments
 (0)