mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 09:00:33 +03:00

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.
350 lines
11 KiB
C++
350 lines
11 KiB
C++
//===-- 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;
|
||
}
|