ldc/gen/binops.cpp
Martin ffaeb09188 Binassign: Always use the lvalue subexpression for the binop lhs
Just cast it to the full lhs type, except for shift-assignments (always
perform the shift op in the lvalue precision).

The front-end likes to cast the lhs to the type it wants for the
intermediate binop. E.g., incrementing a ubyte lvalue by an integer leads
to `lval += 3` being rewritten to `cast(int)lval += 3`.

Now if we use the full lhs value for the binop, we get an rvalue result
due to the cast. I.e., `cast(int)lval` before evaluating the rhs. So what
we actually want is to load the lhs lvalue `lval` AFTER evaluating the rhs
and cast it to an integer so that the binop is performed in that
precision. The result is casted back to the lvalue type when assigning.
2016-07-17 23:28:12 +02:00

350 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- binops.cpp --------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "gen/binops.h"
#include "declaration.h"
#include "gen/complex.h"
#include "gen/dvalue.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
//////////////////////////////////////////////////////////////////////////////
dinteger_t undoStrideMul(Loc &loc, Type *t, dinteger_t offset) {
assert(t->ty == Tpointer);
d_uns64 elemSize = t->nextOf()->size(loc);
assert((offset % elemSize) == 0 &&
"Expected offset by an integer amount of elements");
return offset / elemSize;
}
//////////////////////////////////////////////////////////////////////////////
namespace {
struct RVals {
DRValue *lhs, *rhs;
};
RVals evalSides(Expression *lhs, Expression *rhs, bool loadLhsAfterRhs) {
RVals rvals;
DValue *lhsVal = toElem(lhs);
if (!loadLhsAfterRhs) {
rvals.lhs = lhsVal->getRVal();
rvals.rhs = toElem(rhs)->getRVal();
} else {
rvals.rhs = toElem(rhs)->getRVal();
rvals.lhs = lhsVal->getRVal();
}
return rvals;
}
/// Tries to remove a MulExp by a constant value of baseSize from e. Returns
/// NULL if not possible.
Expression *extractNoStrideInc(Expression *e, d_uns64 baseSize, bool &negate) {
MulExp *mul;
while (true) {
if (e->op == TOKneg) {
negate = !negate;
e = static_cast<NegExp *>(e)->e1;
continue;
}
if (e->op == TOKmul) {
mul = static_cast<MulExp *>(e);
break;
}
return nullptr;
}
if (!mul->e2->isConst()) {
return nullptr;
}
dinteger_t stride = mul->e2->toInteger();
if (stride != baseSize) {
return nullptr;
}
return mul->e1;
}
DValue *emitPointerOffset(Loc loc, Expression *base, Expression *offset,
bool negateOffset, Type *resultType,
bool loadLhsAfterRhs) {
// The operand emitted by the frontend is in units of bytes, and not
// pointer elements. We try to undo this before resorting to
// temporarily bitcasting the pointer to i8.
LLValue *llBase = nullptr;
LLValue *llOffset = nullptr;
LLValue *llResult = nullptr;
if (offset->isConst()) {
llBase = DtoRVal(base);
dinteger_t byteOffset = offset->toInteger();
if (byteOffset == 0) {
llResult = llBase;
} else {
llOffset = DtoConstSize_t(undoStrideMul(loc, base->type, byteOffset));
}
} else {
Expression *noStrideInc = extractNoStrideInc(
offset, base->type->nextOf()->size(loc), negateOffset);
auto rvals =
evalSides(base, noStrideInc ? noStrideInc : offset, loadLhsAfterRhs);
llBase = DtoRVal(rvals.lhs);
llOffset = DtoRVal(rvals.rhs);
if (!noStrideInc) // byte offset => cast base to i8*
llBase = DtoBitCast(llBase, getVoidPtrType());
}
if (!llResult) {
if (negateOffset)
llOffset = gIR->ir->CreateNeg(llOffset);
llResult = DtoGEP1(llBase, llOffset, false);
}
return new DImValue(resultType, DtoBitCast(llResult, DtoType(resultType)));
}
}
//////////////////////////////////////////////////////////////////////////////
DValue *binAdd(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
Type *lhsType = lhs->type->toBasetype();
Type *rhsType = rhs->type->toBasetype();
if (lhsType != rhsType && lhsType->ty == Tpointer && rhsType->isintegral()) {
Logger::println("Adding integer to pointer");
return emitPointerOffset(loc, lhs, rhs, false, type, loadLhsAfterRhs);
}
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
if (type->iscomplex())
return DtoComplexAdd(loc, type, rvals.lhs, rvals.rhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res = (type->isfloating() ? gIR->ir->CreateFAdd(l, r)
: gIR->ir->CreateAdd(l, r));
return new DImValue(type, res);
}
//////////////////////////////////////////////////////////////////////////////
DValue *binMin(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
Type *lhsType = lhs->type->toBasetype();
Type *rhsType = rhs->type->toBasetype();
if (lhsType != rhsType && lhsType->ty == Tpointer && rhsType->isintegral()) {
Logger::println("Subtracting integer from pointer");
return emitPointerOffset(loc, lhs, rhs, true, type, loadLhsAfterRhs);
}
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
if (lhsType->ty == Tpointer && rhsType->ty == Tpointer) {
LLValue *l = DtoRVal(rvals.lhs);
LLValue *r = DtoRVal(rvals.rhs);
LLType *llSizeT = DtoSize_t();
l = gIR->ir->CreatePtrToInt(l, llSizeT);
r = gIR->ir->CreatePtrToInt(r, llSizeT);
LLValue *diff = gIR->ir->CreateSub(l, r);
LLType *llType = DtoType(type);
if (diff->getType() != llType)
diff = gIR->ir->CreateIntToPtr(diff, llType);
return new DImValue(type, diff);
}
if (type->iscomplex())
return DtoComplexMin(loc, type, rvals.lhs, rvals.rhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res = (type->isfloating() ? gIR->ir->CreateFSub(l, r)
: gIR->ir->CreateSub(l, r));
return new DImValue(type, res);
}
//////////////////////////////////////////////////////////////////////////////
DValue *binMul(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
if (type->iscomplex())
return DtoComplexMul(loc, type, rvals.lhs, rvals.rhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res = (type->isfloating() ? gIR->ir->CreateFMul(l, r)
: gIR->ir->CreateMul(l, r));
return new DImValue(type, res);
}
//////////////////////////////////////////////////////////////////////////////
DValue *binDiv(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
if (type->iscomplex())
return DtoComplexDiv(loc, type, rvals.lhs, rvals.rhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res;
if (type->isfloating()) {
res = gIR->ir->CreateFDiv(l, r);
} else if (!isLLVMUnsigned(type)) {
res = gIR->ir->CreateSDiv(l, r);
} else {
res = gIR->ir->CreateUDiv(l, r);
}
return new DImValue(type, res);
}
//////////////////////////////////////////////////////////////////////////////
DValue *binMod(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
if (type->iscomplex())
return DtoComplexMod(loc, type, rvals.lhs, rvals.rhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res;
if (type->isfloating()) {
res = gIR->ir->CreateFRem(l, r);
} else if (!isLLVMUnsigned(type)) {
res = gIR->ir->CreateSRem(l, r);
} else {
res = gIR->ir->CreateURem(l, r);
}
return new DImValue(type, res);
}
//////////////////////////////////////////////////////////////////////////////
namespace {
template <llvm::Instruction::BinaryOps binOp>
DValue *binBitwise(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs);
LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type));
LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type));
LLValue *res = llvm::BinaryOperator::Create(binOp, l, r, "", gIR->scopebb());
return new DImValue(type, res);
}
}
DValue *binAnd(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
return binBitwise<llvm::Instruction::And>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
DValue *binOr(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
return binBitwise<llvm::Instruction::Or>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
DValue *binXor(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
return binBitwise<llvm::Instruction::Xor>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
DValue *binShl(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
return binBitwise<llvm::Instruction::Shl>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
DValue *binShr(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
if (isLLVMUnsigned(type))
return binUshr(loc, type, lhs, rhs);
return binBitwise<llvm::Instruction::AShr>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
DValue *binUshr(Loc &loc, Type *type, Expression *lhs, Expression *rhs,
bool loadLhsAfterRhs) {
return binBitwise<llvm::Instruction::LShr>(loc, type, lhs, rhs,
loadLhsAfterRhs);
}
//////////////////////////////////////////////////////////////////////////////
LLValue *DtoBinNumericEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op) {
assert(op == TOKequal || op == TOKnotequal || op == TOKidentity ||
op == TOKnotidentity);
Type *t = lhs->type->toBasetype();
assert(t->isfloating());
Logger::println("numeric equality");
LLValue *res = nullptr;
if (t->iscomplex()) {
Logger::println("complex");
res = DtoComplexEquals(loc, op, lhs, rhs);
} else if (t->isfloating()) {
Logger::println("floating");
res = DtoBinFloatsEquals(loc, lhs, rhs, op);
}
assert(res);
return res;
}
//////////////////////////////////////////////////////////////////////////////
LLValue *DtoBinFloatsEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op) {
LLValue *res = nullptr;
if (op == TOKequal) {
res = gIR->ir->CreateFCmpOEQ(DtoRVal(lhs), DtoRVal(rhs));
} else if (op == TOKnotequal) {
res = gIR->ir->CreateFCmpUNE(DtoRVal(lhs), DtoRVal(rhs));
} else {
llvm::ICmpInst::Predicate cmpop;
if (op == TOKidentity) {
cmpop = llvm::ICmpInst::ICMP_EQ;
} else {
cmpop = llvm::ICmpInst::ICMP_NE;
}
LLValue *sz = DtoConstSize_t(getTypeStoreSize(DtoType(lhs->type)));
LLValue *val = DtoMemCmp(makeLValue(loc, lhs), makeLValue(loc, rhs), sz);
res = gIR->ir->CreateICmp(cmpop, val,
LLConstantInt::get(val->getType(), 0, false));
}
assert(res);
return res;
}