Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong evaluation order for binary operators #1327

Closed
trikko opened this issue Mar 1, 2016 · 9 comments
Closed

Wrong evaluation order for binary operators #1327

trikko opened this issue Mar 1, 2016 · 9 comments

Comments

@trikko
Copy link
Contributor

trikko commented Mar 1, 2016

import std.stdio;

void main()
{

    size_t inc(size_t* v)
    {
        (*v)++;
        return 10;
    }


    size_t pos = 10;

    immutable tst = pos + inc(&pos); // <-- this
    writeln(tst);
    writeln(pos);
}

This code outputs:
21
11

Instead of:
20
11

With lcd 1.0.0 alpha

@kinke
Copy link
Member

kinke commented Mar 1, 2016

Compile-time only, right?

@trikko
Copy link
Contributor Author

trikko commented Mar 1, 2016

I don't think so.

@dnadlinger
Copy link
Member

It's not compile-time only. Also, from this it seems like we codegen the arguments in LTR order, but fail to immediately load from the pos lvalue:

* * * * * * * DtoVarDeclaration(vdtype = immutable(int))
* * * * * * * * llvm value for decl:   %tst = alloca i32, align 4
* * * * * * * * expression initializer
* * * * * * * * AssignExp::toElem: tst = pos + inc(& pos) | (immutable(int))(immutable(int) = immutable(int))
* * * * * * * * * VarExp::toElem: tst @ immutable(int)
* * * * * * * * * * DtoSymbolAddress ('tst' of type 'immutable(int)')
* * * * * * * * * * * a normal variable
* * * * * * * * * AddExp::toElem: pos + inc(& pos) @ immutable(int)
* * * * * * * * * * VarExp::toElem: pos @ int
* * * * * * * * * * * DtoSymbolAddress ('pos' of type 'int')
* * * * * * * * * * * * a normal variable
* * * * * * * * * * CallExp::toElem: inc(& pos) @ int
* * * * * * * * * * * VarExp::toElem: inc @ int(int* v)
* * * * * * * * * * * * DtoSymbolAddress ('inc' of type 'int(int* v)')
* * * * * * * * * * * * * FuncDeclaration
* * * * * * * * * * * DtoCallFunction()
* * * * * * * * * * * * Building type: int(int* v)
* * * * * * * * * * * * * DtoFunctionType(int(int* v))
* * * * * * * * * * * * * * x86-64 ABI: Transforming return type
* * * * * * * * * * * * * * x86-64 ABI: Transforming argument types
* * * * * * * * * * * * * * Final function type: i32 (i32*)
* * * * * * * * * * * * DtoNestedContext for test.main.inc
* * * * * * * * * * * * * DtoCreateNestedContextType for test.main.inc
* * * * * * * * * * * * * for function of depth -1
* * * * * * * * * * * * * function does not have context or creates its own from scratch, returning undef
* * * * * * * * * * * * doing normal arguments
* * * * * * * * * * * * Arguments so far: (1)
* * * * * * * * * * * * * i8* undef
* * * * * * * * * * * * Function type: int(int* v)
* * * * * * * * * * * * DtoArgument
* * * * * * * * * * * * * SymOffExp::toElem: & pos @ int*
* * * * * * * * * * * * * * DtoSymbolAddress ('pos' of type 'int')
* * * * * * * * * * * * * * * a normal variable
* * * * * * * * * performing normal assignment (rhs has lvalue elems = 0)
* * * * * * * * * DtoAssign()
* * * * * * * * * * lhs:   %tst = alloca i32, align 4
* * * * * * * * * * rhs:   %3 = add i32 %2, %1

That's as far as I got while waiting on the airport; probably won't have time to follow up on it anytime soon.

@kinke
Copy link
Member

kinke commented Mar 1, 2016

I think the problem is that we're doing DValue *lv = toElem(l), then DValue *rv = toElem(r) (in ToElemVisitor::visit(AddExp*)), and only then lv->getRVal() + rv->getRVal() (the latter in DtoBinAdd()).
The load of pos happens in lv->getRVal(), while inc() is already called in toElem(r) (I guess).
So we'd probably need to let DtoBinXXX() take 2 LLValue* instead of DValue*, for toElem(l)->getRVal() and then toElem(r)->getRVal().
There may be more issues like this lurking in the depths...

@kinke
Copy link
Member

kinke commented Mar 4, 2016

Well maybe using a specialization of DValue for function calls, DCallValue or something, which only lazily evaluates the call expression on the first getLVal()/getRVal() call, would be a more elegant and robust approach to this...

@JohanEngelen
Copy link
Member

@kinke I think your proposed fix is correct.

I mean this one:

So we'd probably need to let DtoBinXXX() take 2 LLValue* instead of DValue*, for toElem(l)->getRVal() and then toElem(r)->getRVal().

@kinke
Copy link
Member

kinke commented Mar 25, 2016

So we've just had 2 DValue related evaluation-order issues (1 already fixed by Johan). Iirc, @klickverbot mentioned a few months ago that he's generally not happy with DValue. So we may need to re-think this for a proper solution. I don't have a sufficient overview yet and sadly generally not a lot of time right now (just bought an apartment, got the keys today, will be moving in next week etc.).

@dnadlinger
Copy link
Member

To fix this and related issues, I think what we need to do is to go through the code base and make sure that toElem is (almost) never called without also immediately invoking get{L, R}Val().

Anybody? ;P

@kinke
Copy link
Member

kinke commented Mar 27, 2016

I was thinking about introducing a DtoLVal()/DtoRVal() and hiding toElem. Which obviously also means no more getRVal() and getLVal() for DValue. It simply knows if it's a lvalue or not, i.e., whether its LLValue is a pointer or an immediate.
This would also get rid of quite a few unnecessary loads in the IR, as we sometimes access getRVal() multiple times (e.g., 1st time to get the LLVM type etc.). But I don't have enough time right now.

Edit: DtoRVal() would be somewhat special, in the sense of not always returning an rvalue. If it's a struct or static array, we always keep it in memory, so it would return an lvalue DValue*. This matches the current semantics in DVarValue::getRVal(). Detecting this case (e.g., to perform a memcpy() instead of a store) is trivially accomplished by asking DValue::isLVal().

@dnadlinger dnadlinger changed the title Wrong evaluation order for args Wrong evaluation order for binary operators Apr 1, 2016
kinke added a commit to kinke/ldc that referenced this issue Jun 15, 2016
kinke added a commit to kinke/ldc that referenced this issue Jun 18, 2016
Fixes issue ldc-developers#1327 by loading immediately from lvalues resulting from
left- and right-hand sides.
kinke added a commit to kinke/ldc that referenced this issue Jun 25, 2016
Fixes issue ldc-developers#1327 by loading immediately from lvalues resulting from
left- and right-hand sides.
kinke added a commit to kinke/ldc that referenced this issue Jun 26, 2016
Fixes issue ldc-developers#1327 by loading immediately from lvalues resulting from
left- and right-hand sides.
kinke added a commit to kinke/ldc that referenced this issue Jun 26, 2016
Fixes issue ldc-developers#1327 by loading immediately from lvalues resulting from
left- and right-hand sides.
kinke added a commit that referenced this issue Jun 26, 2016
Continue with DValue refactoring and fix issue #1327
kinke added a commit that referenced this issue Jun 26, 2016
@kinke kinke closed this as completed Jun 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants