mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-12 02:43:14 +01:00
See the comment in DtoCallFunction for an explanation of what is going on. The struct zero initialization code was also refactored out to AssignExp::toElem and modified so that it is only triggered on integer->struct assignments, not for any types where the modifier-stripped types don't match up. This would have lead to silently wrong code in the cases where the assert would have been triggered otherwise. Fixes the Phobos testsuite build.
3290 lines
103 KiB
C++
3290 lines
103 KiB
C++
// Backend stubs
|
|
|
|
/* DMDFE backend stubs
|
|
* This file contains the implementations of the backend routines.
|
|
* For dmdfe these do nothing but print a message saying the module
|
|
* has been parsed. Substitute your own behaviors for these routimes.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <fstream>
|
|
|
|
#include "gen/llvm.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "attrib.h"
|
|
#include "init.h"
|
|
#include "mtype.h"
|
|
#include "template.h"
|
|
#include "hdrgen.h"
|
|
#include "port.h"
|
|
#include "rmem.h"
|
|
#include "id.h"
|
|
#include "enum.h"
|
|
|
|
#include "gen/irstate.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/tollvm.h"
|
|
#include "gen/llvmhelpers.h"
|
|
#include "gen/runtime.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/structs.h"
|
|
#include "gen/classes.h"
|
|
#include "gen/typeinf.h"
|
|
#include "gen/complex.h"
|
|
#include "gen/dvalue.h"
|
|
#include "gen/aa.h"
|
|
#include "gen/functions.h"
|
|
#include "gen/todebug.h"
|
|
#include "gen/nested.h"
|
|
#include "gen/utils.h"
|
|
#include "gen/warnings.h"
|
|
#include "gen/optimizer.h"
|
|
#include "gen/pragma.h"
|
|
|
|
#include "llvm/Support/ManagedStatic.h"
|
|
|
|
llvm::cl::opt<bool> checkPrintf("check-printf-calls",
|
|
llvm::cl::desc("Validate printf call format strings against arguments"),
|
|
llvm::cl::ZeroOrMore);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Expression::cacheLvalue(IRState* irs)
|
|
{
|
|
error("expression %s does not mask any l-value", toChars());
|
|
fatal();
|
|
}
|
|
|
|
/*******************************************
|
|
* Evaluate Expression, then call destructors on any temporaries in it.
|
|
*/
|
|
|
|
DValue *Expression::toElemDtor(IRState *irs)
|
|
{
|
|
#if DMDV2
|
|
Logger::println("Expression::toElemDtor(): %s", toChars());
|
|
size_t starti = irs->varsInScope().size();
|
|
DValue *val = toElem(irs);
|
|
size_t endi = irs->varsInScope().size();
|
|
|
|
// Add destructors
|
|
while (endi-- > starti)
|
|
{
|
|
VarDeclaration *vd = gIR->varsInScope().back();
|
|
gIR->varsInScope().pop_back();
|
|
vd->edtor->toElem(gIR);
|
|
}
|
|
return val;
|
|
#else
|
|
return toElem(irs);
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DeclarationExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("DeclarationExp::toElem: %s | T=%s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
return DtoDeclarationExp(declaration);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void VarExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = toElem(p)->getLVal();
|
|
}
|
|
|
|
DValue* VarExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("VarExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(var);
|
|
|
|
if (cachedLvalue)
|
|
{
|
|
LLValue* V = cachedLvalue;
|
|
return new DVarValue(type, V);
|
|
}
|
|
|
|
if (VarDeclaration* vd = var->isVarDeclaration())
|
|
{
|
|
Logger::println("VarDeclaration ' %s ' of type ' %s '", vd->toChars(), vd->type->toChars());
|
|
|
|
#if DMDV2
|
|
/* The magic variable __ctfe is always false at runtime
|
|
*/
|
|
if (vd->ident == Id::ctfe) {
|
|
return new DConstValue(type, DtoConstBool(false));
|
|
}
|
|
#endif
|
|
|
|
// this is an error! must be accessed with DotVarExp
|
|
if (var->needThis())
|
|
{
|
|
error("need 'this' to access member %s", toChars());
|
|
fatal();
|
|
}
|
|
|
|
// _arguments
|
|
if (vd->ident == Id::_arguments && p->func()->_arguments)
|
|
{
|
|
Logger::println("Id::_arguments");
|
|
LLValue* v = p->func()->_arguments;
|
|
return new DVarValue(type, vd, v);
|
|
}
|
|
// _argptr
|
|
else if (vd->ident == Id::_argptr && p->func()->_argptr)
|
|
{
|
|
Logger::println("Id::_argptr");
|
|
LLValue* v = p->func()->_argptr;
|
|
return new DVarValue(type, vd, v);
|
|
}
|
|
// _dollar
|
|
else if (vd->ident == Id::dollar)
|
|
{
|
|
Logger::println("Id::dollar");
|
|
LLValue* val = 0;
|
|
if (vd->ir.isSet() && (val = vd->ir.getIrValue())) {
|
|
// It must be length of a range
|
|
return new DVarValue(type, vd, val);
|
|
}
|
|
assert(!p->arrays.empty());
|
|
val = DtoArrayLen(p->arrays.back());
|
|
return new DImValue(type, val);
|
|
}
|
|
// classinfo
|
|
else if (ClassInfoDeclaration* cid = vd->isClassInfoDeclaration())
|
|
{
|
|
Logger::println("ClassInfoDeclaration: %s", cid->cd->toChars());
|
|
cid->cd->codegen(Type::sir);;
|
|
return new DVarValue(type, vd, cid->cd->ir.irStruct->getClassInfoSymbol());
|
|
}
|
|
// typeinfo
|
|
else if (TypeInfoDeclaration* tid = vd->isTypeInfoDeclaration())
|
|
{
|
|
Logger::println("TypeInfoDeclaration");
|
|
tid->codegen(Type::sir);
|
|
assert(tid->ir.getIrValue());
|
|
LLType* vartype = DtoType(type);
|
|
LLValue* m = tid->ir.getIrValue();
|
|
if (m->getType() != getPtrToType(vartype))
|
|
m = p->ir->CreateBitCast(m, vartype, "tmp");
|
|
return new DImValue(type, m);
|
|
}
|
|
// nested variable
|
|
#if DMDV2
|
|
else if (vd->nestedrefs.dim) {
|
|
#else
|
|
else if (vd->nestedref) {
|
|
#endif
|
|
Logger::println("nested variable");
|
|
return DtoNestedVariable(loc, type, vd);
|
|
}
|
|
// function parameter
|
|
else if (vd->isParameter()) {
|
|
Logger::println("function param");
|
|
Logger::println("type: %s", vd->type->toChars());
|
|
FuncDeclaration* fd = vd->toParent2()->isFuncDeclaration();
|
|
if (fd && fd != p->func()->decl) {
|
|
Logger::println("nested parameter");
|
|
return DtoNestedVariable(loc, type, vd);
|
|
}
|
|
else if (vd->storage_class & STClazy) {
|
|
Logger::println("lazy parameter");
|
|
assert(type->ty == Tdelegate);
|
|
return new DVarValue(type, vd->ir.getIrValue());
|
|
}
|
|
else if (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type) || llvm::isa<llvm::AllocaInst>(vd->ir.getIrValue())) {
|
|
return new DVarValue(type, vd, vd->ir.getIrValue());
|
|
}
|
|
else if (llvm::isa<llvm::Argument>(vd->ir.getIrValue())) {
|
|
return new DImValue(type, vd->ir.getIrValue());
|
|
}
|
|
else assert(0);
|
|
}
|
|
else {
|
|
Logger::println("a normal variable");
|
|
|
|
// take care of forward references of global variables
|
|
if (vd->isDataseg() || (vd->storage_class & STCextern))
|
|
vd->codegen(Type::sir);
|
|
|
|
LLValue* val;
|
|
|
|
if (!vd->ir.isSet() || !(val = vd->ir.getIrValue())) {
|
|
// FIXME: this error is bad!
|
|
// We should be VERY careful about adding errors in general, as they have
|
|
// a tendency to "mask" out the underlying problems ...
|
|
error("variable %s not resolved", vd->toChars());
|
|
if (Logger::enabled())
|
|
Logger::cout() << "unresolved variable had type: " << *DtoType(vd->type) << '\n';
|
|
fatal();
|
|
}
|
|
|
|
if (vd->isDataseg() || (vd->storage_class & STCextern))
|
|
val = DtoBitCast(val, DtoType(type->pointerTo()));
|
|
|
|
return new DVarValue(type, vd, val);
|
|
}
|
|
}
|
|
else if (FuncDeclaration* fdecl = var->isFuncDeclaration())
|
|
{
|
|
Logger::println("FuncDeclaration");
|
|
LLValue* func = 0;
|
|
#if DMDV2
|
|
fdecl = fdecl->toAliasFunc();
|
|
#endif
|
|
if (fdecl->llvmInternal == LLVMinline_asm) {
|
|
error("special ldc inline asm is not a normal function");
|
|
fatal();
|
|
}
|
|
else if (fdecl->llvmInternal != LLVMva_arg) {
|
|
fdecl->codegen(Type::sir);
|
|
func = fdecl->ir.irFunc->func;
|
|
}
|
|
return new DFuncValue(fdecl, func);
|
|
}
|
|
else if (StaticStructInitDeclaration* sdecl = var->isStaticStructInitDeclaration())
|
|
{
|
|
// this seems to be the static initialiser for structs
|
|
Type* sdecltype = sdecl->type->toBasetype();
|
|
Logger::print("Sym: type=%s\n", sdecltype->toChars());
|
|
assert(sdecltype->ty == Tstruct);
|
|
TypeStruct* ts = static_cast<TypeStruct*>(sdecltype);
|
|
assert(ts->sym);
|
|
ts->sym->codegen(Type::sir);
|
|
|
|
LLValue* initsym = ts->sym->ir.irStruct->getInitSymbol();
|
|
initsym = DtoBitCast(initsym, DtoType(ts->pointerTo()));
|
|
return new DVarValue(type, initsym);
|
|
}
|
|
else
|
|
{
|
|
assert(0 && "Unimplemented VarExp type");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* VarExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("VarExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if (StaticStructInitDeclaration* sdecl = var->isStaticStructInitDeclaration())
|
|
{
|
|
// this seems to be the static initialiser for structs
|
|
Type* sdecltype = sdecl->type->toBasetype();
|
|
Logger::print("Sym: type=%s\n", sdecltype->toChars());
|
|
assert(sdecltype->ty == Tstruct);
|
|
TypeStruct* ts = static_cast<TypeStruct*>(sdecltype);
|
|
ts->sym->codegen(Type::sir);
|
|
|
|
return ts->sym->ir.irStruct->getDefaultInit();
|
|
}
|
|
|
|
if (TypeInfoDeclaration* ti = var->isTypeInfoDeclaration())
|
|
{
|
|
LLType* vartype = DtoType(type);
|
|
LLConstant* m = DtoTypeInfoOf(ti->tinfo, false);
|
|
if (m->getType() != getPtrToType(vartype))
|
|
m = llvm::ConstantExpr::getBitCast(m, vartype);
|
|
return m;
|
|
}
|
|
|
|
VarDeclaration* vd = var->isVarDeclaration();
|
|
if (vd && vd->isConst() && vd->init)
|
|
{
|
|
if (vd->inuse)
|
|
{
|
|
error("recursive reference %s", toChars());
|
|
return llvm::UndefValue::get(DtoType(type));
|
|
}
|
|
vd->inuse++;
|
|
LLConstant* ret = DtoConstInitializer(loc, type, vd->init);
|
|
vd->inuse--;
|
|
// return the initializer
|
|
return ret;
|
|
}
|
|
|
|
// fail
|
|
error("non-constant expression %s", toChars());
|
|
return llvm::UndefValue::get(DtoType(type));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* IntegerExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("IntegerExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
LLConstant* c = toConstElem(p);
|
|
return new DConstValue(type, c);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* IntegerExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("IntegerExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
LLType* t = DtoType(type);
|
|
if (isaPointer(t)) {
|
|
Logger::println("pointer");
|
|
LLConstant* i = LLConstantInt::get(DtoSize_t(),(uint64_t)value,false);
|
|
return llvm::ConstantExpr::getIntToPtr(i, t);
|
|
}
|
|
assert(llvm::isa<LLIntegerType>(t));
|
|
LLConstant* c = LLConstantInt::get(t,(uint64_t)value,!type->isunsigned());
|
|
assert(c);
|
|
if (Logger::enabled())
|
|
Logger::cout() << "value = " << *c << '\n';
|
|
return c;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* RealExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("RealExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
LLConstant* c = toConstElem(p);
|
|
return new DConstValue(type, c);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* RealExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("RealExp::toConstElem: %s @ %s | %La\n", toChars(), type->toChars(), value);
|
|
LOG_SCOPE;
|
|
Type* t = type->toBasetype();
|
|
return DtoConstFP(t, value);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* NullExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("NullExp::toElem(type=%s): %s\n", type->toChars(),toChars());
|
|
LOG_SCOPE;
|
|
LLConstant* c = toConstElem(p);
|
|
return new DNullValue(type, c);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* NullExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("NullExp::toConstElem(type=%s): %s\n", type->toChars(),toChars());
|
|
LOG_SCOPE;
|
|
LLType* t = DtoType(type);
|
|
if (type->ty == Tarray) {
|
|
assert(isaStruct(t));
|
|
return llvm::ConstantAggregateZero::get(t);
|
|
}
|
|
else {
|
|
return LLConstant::getNullValue(t);
|
|
}
|
|
assert(0);
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ComplexExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ComplexExp::toElem(): %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
LLConstant* c = toConstElem(p);
|
|
LLValue* res;
|
|
|
|
if (c->isNullValue()) {
|
|
Type* t = type->toBasetype();
|
|
if (t->ty == Tcomplex32)
|
|
c = DtoConstFP(Type::tfloat32, ldouble(0));
|
|
else if (t->ty == Tcomplex64)
|
|
c = DtoConstFP(Type::tfloat64, ldouble(0));
|
|
else if (t->ty == Tcomplex80)
|
|
c = DtoConstFP(Type::tfloat80, ldouble(0));
|
|
else
|
|
assert(0);
|
|
res = DtoAggrPair(DtoType(type), c, c);
|
|
}
|
|
else {
|
|
res = DtoAggrPair(DtoType(type), c->getOperand(0), c->getOperand(1));
|
|
}
|
|
|
|
return new DImValue(type, res);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* ComplexExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("ComplexExp::toConstElem(): %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
return DtoConstComplex(type, value.re, value.im);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename T>
|
|
static inline LLConstant* toConstantArray(LLType* ct, LLArrayType* at, T* str, size_t len, bool nullterm = true)
|
|
{
|
|
std::vector<LLConstant*> vals;
|
|
vals.reserve(len+1);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
vals.push_back(LLConstantInt::get(ct, str[i], false));
|
|
}
|
|
if (nullterm)
|
|
vals.push_back(LLConstantInt::get(ct, 0, false));
|
|
return LLConstantArray::get(at, vals);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* StringExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("StringExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
Type* dtype = type->toBasetype();
|
|
Type* cty = dtype->nextOf()->toBasetype();
|
|
|
|
LLType* ct = DtoTypeNotVoid(cty);
|
|
//printf("ct = %s\n", type->nextOf()->toChars());
|
|
LLArrayType* at = LLArrayType::get(ct,len+1);
|
|
|
|
LLConstant* _init;
|
|
switch (cty->size())
|
|
{
|
|
default:
|
|
llvm_unreachable("Unknown char type");
|
|
case 1:
|
|
_init = toConstantArray(ct, at, static_cast<uint8_t *>(string), len);
|
|
break;
|
|
case 2:
|
|
_init = toConstantArray(ct, at, static_cast<uint16_t *>(string), len);
|
|
break;
|
|
case 4:
|
|
_init = toConstantArray(ct, at, static_cast<uint32_t *>(string), len);
|
|
break;
|
|
}
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::InternalLinkage;
|
|
if (Logger::enabled())
|
|
Logger::cout() << "type: " << *at << "\ninit: " << *_init << '\n';
|
|
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(*gIR->module,at,true,_linkage,_init,".str");
|
|
|
|
llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false);
|
|
LLConstant* idxs[2] = { zero, zero };
|
|
LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true);
|
|
|
|
if (dtype->ty == Tarray) {
|
|
LLConstant* clen = LLConstantInt::get(DtoSize_t(),len,false);
|
|
return new DImValue(type, DtoConstSlice(clen, arrptr, dtype));
|
|
}
|
|
else if (dtype->ty == Tsarray) {
|
|
LLType* dstType = getPtrToType(LLArrayType::get(ct, len));
|
|
LLValue* emem = (gvar->getType() == dstType) ? gvar : DtoBitCast(gvar, dstType);
|
|
return new DVarValue(type, emem);
|
|
}
|
|
else if (dtype->ty == Tpointer) {
|
|
return new DImValue(type, arrptr);
|
|
}
|
|
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* StringExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("StringExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
Type* t = type->toBasetype();
|
|
Type* cty = t->nextOf()->toBasetype();
|
|
|
|
bool nullterm = (t->ty != Tsarray);
|
|
size_t endlen = nullterm ? len+1 : len;
|
|
|
|
LLType* ct = DtoTypeNotVoid(cty);
|
|
LLArrayType* at = LLArrayType::get(ct,endlen);
|
|
|
|
LLConstant* _init;
|
|
switch (cty->size())
|
|
{
|
|
default:
|
|
llvm_unreachable("Unknown char type");
|
|
case 1:
|
|
_init = toConstantArray(ct, at, static_cast<uint8_t *>(string), len, nullterm);
|
|
break;
|
|
case 2:
|
|
_init = toConstantArray(ct, at, static_cast<uint16_t *>(string), len, nullterm);
|
|
break;
|
|
case 4:
|
|
_init = toConstantArray(ct, at, static_cast<uint32_t *>(string), len, nullterm);
|
|
break;
|
|
}
|
|
|
|
if (t->ty == Tsarray)
|
|
{
|
|
return _init;
|
|
}
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::InternalLinkage;
|
|
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(*gIR->module,_init->getType(),true,_linkage,_init,".str");
|
|
|
|
llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false);
|
|
LLConstant* idxs[2] = { zero, zero };
|
|
LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true);
|
|
|
|
if (t->ty == Tpointer) {
|
|
return arrptr;
|
|
}
|
|
else if (t->ty == Tarray) {
|
|
LLConstant* clen = LLConstantInt::get(DtoSize_t(),len,false);
|
|
return DtoConstSlice(clen, arrptr, type);
|
|
}
|
|
|
|
assert(0);
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AssignExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", toChars(), type->toChars(), e1->type->toChars(), e2->type ? e2->type->toChars() : 0);
|
|
LOG_SCOPE;
|
|
|
|
if (e1->op == TOKarraylength)
|
|
{
|
|
Logger::println("performing array.length assignment");
|
|
ArrayLengthExp *ale = static_cast<ArrayLengthExp *>(e1);
|
|
DValue* arr = ale->e1->toElem(p);
|
|
DVarValue arrval(ale->e1->type, arr->getLVal());
|
|
DValue* newlen = e2->toElem(p);
|
|
DSliceValue* slice = DtoResizeDynArray(arrval.getType(), &arrval, newlen->getRVal());
|
|
DtoAssign(loc, &arrval, slice);
|
|
return newlen;
|
|
}
|
|
|
|
// Can't just override ConstructExp::toElem because not all TOKconstruct
|
|
// operations are actually instances of ConstructExp... Long live the DMD
|
|
// coding style!
|
|
if (op == TOKconstruct)
|
|
{
|
|
if (e1->op == TOKvar)
|
|
{
|
|
VarExp* ve = (VarExp*)e1;
|
|
if (ve->var->storage_class & STCref)
|
|
{
|
|
// Note that the variable value is accessed directly (instead
|
|
// of via getLValue(), which would perform a load from the
|
|
// uninitialized location), and that rhs is stored as an l-value!
|
|
|
|
IrLocal* const local = ve->var->ir.irLocal;
|
|
assert(local && "ref var must be local and already initialized");
|
|
|
|
DValue* rhs = e2->toElem(p);
|
|
DtoStore(rhs->getLVal(), local->value);
|
|
return rhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
if (e1->type->toBasetype()->ty == Tstruct && e2->op == TOKint64)
|
|
{
|
|
Logger::println("performing aggregate zero initialization");
|
|
assert(e2->toInteger() == 0);
|
|
DtoAggrZeroInit(l->getLVal());
|
|
#if DMDV2
|
|
TypeStruct *ts = static_cast<TypeStruct*>(e1->type);
|
|
if (ts->sym->isNested() && ts->sym->vthis)
|
|
DtoResolveNestedContext(loc, ts->sym, l->getLVal());
|
|
#endif
|
|
// Return value should be irrelevant.
|
|
return r;
|
|
}
|
|
|
|
Logger::println("performing normal assignment");
|
|
DtoAssign(loc, l, r, op);
|
|
|
|
if (l->isSlice())
|
|
return l;
|
|
|
|
return r;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// Finds the proper lvalue for a binassign expressions.
|
|
/// Makes sure the given LHS expression is only evaluated once.
|
|
static Expression* findLvalue(IRState* irs, Expression* exp)
|
|
{
|
|
Expression* e = exp;
|
|
|
|
// skip past any casts
|
|
while(e->op == TOKcast)
|
|
e = static_cast<CastExp*>(e)->e1;
|
|
|
|
// cache lvalue and return
|
|
e->cacheLvalue(irs);
|
|
return e;
|
|
}
|
|
|
|
#define BIN_ASSIGN(X) \
|
|
DValue* X##AssignExp::toElem(IRState* p) \
|
|
{ \
|
|
Logger::print(#X"AssignExp::toElem: %s @ %s\n", toChars(), type->toChars()); \
|
|
LOG_SCOPE; \
|
|
X##Exp e3(loc, e1, e2); \
|
|
e3.type = e1->type; \
|
|
DValue* dst = findLvalue(p, e1)->toElem(p); \
|
|
DValue* res = e3.toElem(p); \
|
|
/* Now that we are done with the expression, clear the cached lvalue. */ \
|
|
Expression* e = e1; \
|
|
while(e->op == TOKcast) \
|
|
e = static_cast<CastExp*>(e)->e1; \
|
|
e->cachedLvalue = NULL; \
|
|
/* Assign the (casted) value and return it. */ \
|
|
DValue* stval = DtoCast(loc, res, dst->getType()); \
|
|
DtoAssign(loc, dst, stval); \
|
|
return DtoCast(loc, res, type); \
|
|
}
|
|
|
|
BIN_ASSIGN(Add)
|
|
BIN_ASSIGN(Min)
|
|
BIN_ASSIGN(Mul)
|
|
BIN_ASSIGN(Div)
|
|
BIN_ASSIGN(Mod)
|
|
BIN_ASSIGN(And)
|
|
BIN_ASSIGN(Or)
|
|
BIN_ASSIGN(Xor)
|
|
BIN_ASSIGN(Shl)
|
|
BIN_ASSIGN(Shr)
|
|
BIN_ASSIGN(Ushr)
|
|
|
|
#undef BIN_ASSIGN
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void errorOnIllegalArrayOp(Expression* base, Expression* e1, Expression* e2)
|
|
{
|
|
Type* t1 = e1->type->toBasetype();
|
|
Type* t2 = e2->type->toBasetype();
|
|
|
|
// valid array ops would have been transformed by optimize
|
|
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
|
|
(t2->ty == Tarray || t2->ty == Tsarray)
|
|
)
|
|
{
|
|
base->error("Array operation %s not recognized", base->toChars());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* AddExp::toConstElem(IRState* p)
|
|
{
|
|
// add to pointer
|
|
if (e1->type->ty == Tpointer && e2->type->isintegral()) {
|
|
LLConstant *ptr = e1->toConstElem(p);
|
|
LLConstant *index = e2->toConstElem(p);
|
|
ptr = llvm::ConstantExpr::getGetElementPtr(ptr, llvm::makeArrayRef(&index, 1));
|
|
return ptr;
|
|
}
|
|
|
|
error("expression '%s' is not a constant", toChars());
|
|
fatal();
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AddExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("AddExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
Type* t = type->toBasetype();
|
|
Type* e1type = e1->type->toBasetype();
|
|
Type* e2type = e2->type->toBasetype();
|
|
|
|
errorOnIllegalArrayOp(this, e1, e2);
|
|
|
|
if (e1type != e2type && e1type->ty == Tpointer) {
|
|
Logger::println("add to pointer");
|
|
if (DConstValue* cv = r->isConst()) {
|
|
if (cv->c->isNullValue()) {
|
|
Logger::println("is zero");
|
|
return new DImValue(type, l->getRVal());
|
|
}
|
|
}
|
|
LLValue* v = llvm::GetElementPtrInst::Create(l->getRVal(), r->getRVal(), "tmp", p->scopebb());
|
|
return new DImValue(type, v);
|
|
}
|
|
else if (t->iscomplex()) {
|
|
return DtoComplexAdd(loc, type, l, r);
|
|
}
|
|
else {
|
|
return DtoBinAdd(l,r);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* MinExp::toConstElem(IRState* p)
|
|
{
|
|
if (e1->type->ty == Tpointer && e2->type->isintegral()) {
|
|
LLConstant *ptr = e1->toConstElem(p);
|
|
LLConstant *index = e2->toConstElem(p);
|
|
index = llvm::ConstantExpr::getNeg(index);
|
|
ptr = llvm::ConstantExpr::getGetElementPtr(ptr, llvm::makeArrayRef(&index, 1));
|
|
return ptr;
|
|
}
|
|
|
|
error("expression '%s' is not a constant", toChars());
|
|
fatal();
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* MinExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("MinExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
Type* t = type->toBasetype();
|
|
Type* t1 = e1->type->toBasetype();
|
|
Type* t2 = e2->type->toBasetype();
|
|
|
|
errorOnIllegalArrayOp(this, e1, e2);
|
|
|
|
if (t1->ty == Tpointer && t2->ty == Tpointer) {
|
|
LLValue* lv = l->getRVal();
|
|
LLValue* rv = r->getRVal();
|
|
if (Logger::enabled())
|
|
Logger::cout() << "lv: " << *lv << " rv: " << *rv << '\n';
|
|
lv = p->ir->CreatePtrToInt(lv, DtoSize_t(), "tmp");
|
|
rv = p->ir->CreatePtrToInt(rv, DtoSize_t(), "tmp");
|
|
LLValue* diff = p->ir->CreateSub(lv,rv,"tmp");
|
|
if (diff->getType() != DtoType(type))
|
|
diff = p->ir->CreateIntToPtr(diff, DtoType(type), "tmp");
|
|
return new DImValue(type, diff);
|
|
}
|
|
else if (t1->ty == Tpointer) {
|
|
LLValue* idx = p->ir->CreateNeg(r->getRVal(), "tmp");
|
|
LLValue* v = llvm::GetElementPtrInst::Create(l->getRVal(), idx, "tmp", p->scopebb());
|
|
return new DImValue(type, v);
|
|
}
|
|
else if (t->iscomplex()) {
|
|
return DtoComplexSub(loc, type, l, r);
|
|
}
|
|
else {
|
|
return DtoBinSub(l,r);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* MulExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("MulExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
errorOnIllegalArrayOp(this, e1, e2);
|
|
|
|
if (type->iscomplex()) {
|
|
return DtoComplexMul(loc, type, l, r);
|
|
}
|
|
|
|
return DtoBinMul(type, l, r);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DivExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("DivExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
errorOnIllegalArrayOp(this, e1, e2);
|
|
|
|
if (type->iscomplex()) {
|
|
return DtoComplexDiv(loc, type, l, r);
|
|
}
|
|
|
|
return DtoBinDiv(type, l, r);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ModExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ModExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
errorOnIllegalArrayOp(this, e1, e2);
|
|
|
|
if (type->iscomplex()) {
|
|
return DtoComplexRem(loc, type, l, r);
|
|
}
|
|
|
|
return DtoBinRem(type, l, r);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CallExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = toElem(p)->getLVal();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CallExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CallExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if (cachedLvalue)
|
|
{
|
|
LLValue* V = cachedLvalue;
|
|
return new DVarValue(type, V);
|
|
}
|
|
|
|
// handle magic inline asm
|
|
if (e1->op == TOKvar)
|
|
{
|
|
VarExp* ve = static_cast<VarExp*>(e1);
|
|
if (FuncDeclaration* fd = ve->var->isFuncDeclaration())
|
|
{
|
|
if (fd->llvmInternal == LLVMinline_asm)
|
|
{
|
|
return DtoInlineAsmExpr(loc, fd, arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the callee value
|
|
DValue* fnval = e1->toElem(p);
|
|
|
|
// get func value if any
|
|
DFuncValue* dfnval = fnval->isFunc();
|
|
|
|
// handle magic intrinsics (mapping to instructions)
|
|
if (dfnval && dfnval->func)
|
|
{
|
|
FuncDeclaration* fndecl = dfnval->func;
|
|
|
|
// as requested by bearophile, see if it's a C printf call and that it's valid.
|
|
if (global.params.warnings && checkPrintf)
|
|
{
|
|
if (fndecl->linkage == LINKc && strcmp(fndecl->ident->string, "printf") == 0)
|
|
{
|
|
warnInvalidPrintfCall(loc, static_cast<Expression*>(arguments->data[0]), arguments->dim);
|
|
}
|
|
}
|
|
|
|
// va_start instruction
|
|
if (fndecl->llvmInternal == LLVMva_start) {
|
|
if (arguments->dim != 2) {
|
|
error("va_start instruction expects 2 arguments");
|
|
return NULL;
|
|
}
|
|
// llvm doesn't need the second param hence the override
|
|
Expression* exp = static_cast<Expression*>(arguments->data[0]);
|
|
LLValue* arg = exp->toElem(p)->getLVal();
|
|
#if DMDV2
|
|
if (LLValue *argptr = gIR->func()->_argptr) {
|
|
DtoStore(DtoLoad(argptr), DtoBitCast(arg, getPtrToType(getVoidPtrType())));
|
|
return new DImValue(type, arg);
|
|
} else if (global.params.cpu == ARCHx86_64) {
|
|
LLValue *va_list = DtoAlloca(exp->type->nextOf());
|
|
DtoStore(va_list, arg);
|
|
va_list = DtoBitCast(va_list, getVoidPtrType());
|
|
return new DImValue(type, gIR->ir->CreateCall(GET_INTRINSIC_DECL(vastart), va_list, ""));
|
|
} else
|
|
#endif
|
|
{
|
|
arg = DtoBitCast(arg, getVoidPtrType());
|
|
return new DImValue(type, gIR->ir->CreateCall(GET_INTRINSIC_DECL(vastart), arg, ""));
|
|
}
|
|
}
|
|
#if DMDV2
|
|
else if (fndecl->llvmInternal == LLVMva_copy && global.params.cpu == ARCHx86_64) {
|
|
if (arguments->dim != 2) {
|
|
error("va_copy instruction expects 2 arguments");
|
|
return NULL;
|
|
}
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
LLValue* arg1 = exp1->toElem(p)->getLVal();
|
|
LLValue* arg2 = exp2->toElem(p)->getLVal();
|
|
|
|
LLValue *va_list = DtoAlloca(exp1->type->nextOf());
|
|
DtoStore(va_list, arg1);
|
|
|
|
DtoStore(DtoLoad(DtoLoad(arg2)), DtoLoad(arg1));
|
|
return new DVarValue(type, arg1);
|
|
}
|
|
#endif
|
|
// va_arg instruction
|
|
else if (fndecl->llvmInternal == LLVMva_arg) {
|
|
if (arguments->dim != 1) {
|
|
error("va_arg instruction expects 1 arguments");
|
|
return NULL;
|
|
}
|
|
return DtoVaArg(loc, type, static_cast<Expression*>(arguments->data[0]));
|
|
}
|
|
// C alloca
|
|
else if (fndecl->llvmInternal == LLVMalloca) {
|
|
if (arguments->dim != 1) {
|
|
error("alloca expects 1 arguments");
|
|
return NULL;
|
|
}
|
|
Expression* exp = static_cast<Expression*>(arguments->data[0]);
|
|
DValue* expv = exp->toElem(p);
|
|
if (expv->getType()->toBasetype()->ty != Tint32)
|
|
expv = DtoCast(loc, expv, Type::tint32);
|
|
return new DImValue(type, p->ir->CreateAlloca(LLType::getInt8Ty(gIR->context()), expv->getRVal(), ".alloca"));
|
|
}
|
|
// shufflevector
|
|
else if (fndecl->llvmInternal == LLVMshufflevector) {
|
|
llvm::SmallVector<llvm::Constant*, 32> mask;
|
|
for(int i = 2, n = arguments->dim; i < n; i++){
|
|
Expression* exp = static_cast<Expression*>(arguments->data[i]);
|
|
if(exp->op != TOKint64){
|
|
error("Function %s was declared with pragma shufflevector. Because of that all of its arguments except the first two must be integer literals.", fndecl->toChars());
|
|
fatal();
|
|
}
|
|
IntegerExp* iexp = static_cast<IntegerExp*>(arguments->data[i]);
|
|
mask.push_back(iexp->toConstElem(p));
|
|
}
|
|
LLValue* maskVal = llvm::ConstantVector::get(mask);
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
LLValue* v1 = exp1->toElem(p)->getRVal();
|
|
LLValue* v2 = exp2->toElem(p)->getRVal();
|
|
return new DImValue(type, p->ir->CreateShuffleVector(v1, v2, maskVal));
|
|
}
|
|
// fence instruction
|
|
else if (fndecl->llvmInternal == LLVMfence) {
|
|
if (arguments->dim != 1) {
|
|
error("fence instruction expects 1 arguments");
|
|
return NULL;
|
|
}
|
|
gIR->ir->CreateFence(llvm::AtomicOrdering(static_cast<Expression*>(arguments->data[0])->toInteger()));
|
|
return NULL;
|
|
// atomic store instruction
|
|
} else if (fndecl->llvmInternal == LLVMatomic_store) {
|
|
if (arguments->dim != 3) {
|
|
error("atomic store instruction expects 3 arguments");
|
|
return NULL;
|
|
}
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
int atomicOrdering = static_cast<Expression*>(arguments->data[2])->toInteger();
|
|
LLValue* val = exp1->toElem(p)->getRVal();
|
|
LLValue* ptr = exp2->toElem(p)->getRVal();
|
|
llvm::StoreInst* ret = gIR->ir->CreateStore(val, ptr, "tmp");
|
|
ret->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
|
ret->setAlignment(getTypeAllocSize(val->getType()));
|
|
return NULL;
|
|
// atomic load instruction
|
|
} else if (fndecl->llvmInternal == LLVMatomic_load) {
|
|
if (arguments->dim != 2) {
|
|
error("atomic load instruction expects 2 arguments");
|
|
return NULL;
|
|
}
|
|
Expression* exp = static_cast<Expression*>(arguments->data[0]);
|
|
int atomicOrdering = static_cast<Expression*>(arguments->data[1])->toInteger();
|
|
LLValue* ptr = exp->toElem(p)->getRVal();
|
|
Type* retType = exp->type->nextOf();
|
|
llvm::LoadInst* val = gIR->ir->CreateLoad(ptr, "tmp");
|
|
val->setAlignment(getTypeAllocSize(val->getType()));
|
|
val->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
|
return new DImValue(retType, val);
|
|
// cmpxchg instruction
|
|
} else if (fndecl->llvmInternal == LLVMatomic_cmp_xchg) {
|
|
if (arguments->dim != 4) {
|
|
error("cmpxchg instruction expects 4 arguments");
|
|
return NULL;
|
|
}
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
Expression* exp3 = static_cast<Expression*>(arguments->data[2]);
|
|
int atomicOrdering = static_cast<Expression*>(arguments->data[3])->toInteger();
|
|
LLValue* ptr = exp1->toElem(p)->getRVal();
|
|
LLValue* cmp = exp2->toElem(p)->getRVal();
|
|
LLValue* val = exp3->toElem(p)->getRVal();
|
|
LLValue* ret = gIR->ir->CreateAtomicCmpXchg(ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering));
|
|
return new DImValue(exp3->type, ret);
|
|
// atomicrmw instruction
|
|
} else if (fndecl->llvmInternal == LLVMatomic_rmw) {
|
|
if (arguments->dim != 3) {
|
|
error("atomic_rmw instruction expects 3 arguments");
|
|
return NULL;
|
|
}
|
|
|
|
static char *ops[] = {
|
|
"xchg",
|
|
"add",
|
|
"sub",
|
|
"and",
|
|
"nand",
|
|
"or",
|
|
"xor",
|
|
"max",
|
|
"min",
|
|
"umax",
|
|
"umin",
|
|
0
|
|
};
|
|
|
|
int op = 0;
|
|
for (; ; ++op) {
|
|
if (ops[op] == 0) {
|
|
error("unknown atomic_rmw operation %s", fndecl->intrinsicName.c_str());
|
|
return NULL;
|
|
}
|
|
if (fndecl->intrinsicName == ops[op])
|
|
break;
|
|
}
|
|
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
int atomicOrdering = static_cast<Expression*>(arguments->data[2])->toInteger();
|
|
LLValue* ptr = exp1->toElem(p)->getRVal();
|
|
LLValue* val = exp2->toElem(p)->getRVal();
|
|
LLValue* ret = gIR->ir->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp(op), ptr, val,
|
|
llvm::AtomicOrdering(atomicOrdering));
|
|
return new DImValue(exp2->type, ret);
|
|
// bitop
|
|
} else if (fndecl->llvmInternal == LLVMbitop_bt ||
|
|
fndecl->llvmInternal == LLVMbitop_btr ||
|
|
fndecl->llvmInternal == LLVMbitop_btc ||
|
|
fndecl->llvmInternal == LLVMbitop_bts)
|
|
{
|
|
if (arguments->dim != 2) {
|
|
error("bitop intrinsic expects 2 arguments");
|
|
return NULL;
|
|
}
|
|
|
|
Expression* exp1 = static_cast<Expression*>(arguments->data[0]);
|
|
Expression* exp2 = static_cast<Expression*>(arguments->data[1]);
|
|
LLValue* ptr = exp1->toElem(p)->getRVal();
|
|
LLValue* bitnum = exp2->toElem(p)->getRVal();
|
|
|
|
// auto q = cast(ubyte*)ptr + (bitnum >> 3);
|
|
LLValue* q = DtoBitCast(ptr, DtoType(Type::tuns8->pointerTo()));
|
|
q = DtoGEP1(q, p->ir->CreateLShr(bitnum, 3), "bitop.q");
|
|
|
|
// auto mask = 1 << (bitnum & 7);
|
|
LLValue* mask = p->ir->CreateAnd(bitnum, DtoConstSize_t(7), "bitop.tmp");
|
|
mask = p->ir->CreateShl(DtoConstSize_t(1), mask, "bitop.mask");
|
|
|
|
// auto result = (*q & mask) ? -1 : 0;
|
|
LLValue* val = p->ir->CreateZExt(DtoLoad(q, "bitop.tmp"), DtoSize_t(), "bitop.val");
|
|
LLValue* result = p->ir->CreateAnd(val, mask, "bitop.tmp");
|
|
result = p->ir->CreateICmpNE(result, DtoConstSize_t(0), "bitop.tmp");
|
|
result = p->ir->CreateSelect(result, DtoConstInt(-1), DtoConstInt(0), "bitop.result");
|
|
|
|
if (fndecl->llvmInternal != LLVMbitop_bt) {
|
|
llvm::Instruction::BinaryOps op;
|
|
if (fndecl->llvmInternal == LLVMbitop_btc) {
|
|
// *q ^= mask;
|
|
op = llvm::Instruction::Xor;
|
|
} else if (fndecl->llvmInternal == LLVMbitop_btr) {
|
|
// *q &= ~mask;
|
|
mask = p->ir->CreateNot(mask);
|
|
op = llvm::Instruction::And;
|
|
} else if (fndecl->llvmInternal == LLVMbitop_bts) {
|
|
// *q |= mask;
|
|
op = llvm::Instruction::Or;
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
LLValue *newVal = p->ir->CreateBinOp(op, val, mask, "bitop.new_val");
|
|
newVal = p->ir->CreateTrunc(newVal, DtoType(Type::tuns8), "bitop.tmp");
|
|
DtoStore(newVal, q);
|
|
}
|
|
|
|
return new DImValue(type, result);
|
|
}
|
|
}
|
|
return DtoCallFunction(loc, type, fnval, arguments);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CastExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CastExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// get the value to cast
|
|
DValue* u = e1->toElem(p);
|
|
|
|
// cast it to the 'to' type, if necessary
|
|
DValue* v = u;
|
|
if (!to->equals(e1->type))
|
|
v = DtoCast(loc, u, to);
|
|
|
|
// paint the type, if necessary
|
|
if (!type->equals(to))
|
|
v = DtoPaintType(loc, v, type);
|
|
|
|
// return the new rvalue
|
|
return v;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* CastExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("CastExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
LLConstant* res;
|
|
LLType* lltype = DtoType(type);
|
|
Type* tb = to->toBasetype();
|
|
|
|
// string literal to dyn array:
|
|
// reinterpret the string data as an array, calculate the length
|
|
if (e1->op == TOKstring && tb->ty == Tarray) {
|
|
/* StringExp *strexp = static_cast<StringExp*>(e1);
|
|
size_t datalen = strexp->sz * strexp->len;
|
|
Type* eltype = tb->nextOf()->toBasetype();
|
|
if (datalen % eltype->size() != 0) {
|
|
error("the sizes don't line up");
|
|
return e1->toConstElem(p);
|
|
}
|
|
size_t arrlen = datalen / eltype->size();*/
|
|
error("ct cast of string to dynamic array not fully implemented");
|
|
return e1->toConstElem(p);
|
|
}
|
|
// pointer to pointer
|
|
else if (tb->ty == Tpointer && e1->type->toBasetype()->ty == Tpointer) {
|
|
res = llvm::ConstantExpr::getBitCast(e1->toConstElem(p), lltype);
|
|
}
|
|
// global variable to pointer
|
|
else if (tb->ty == Tpointer && e1->op == TOKvar) {
|
|
VarDeclaration *vd = static_cast<VarExp*>(e1)->var->isVarDeclaration();
|
|
assert(vd);
|
|
vd->codegen(Type::sir);
|
|
LLConstant *value = vd->ir.irGlobal ? isaConstant(vd->ir.irGlobal->value) : 0;
|
|
if (!value)
|
|
goto Lerr;
|
|
Type *type = vd->type->toBasetype();
|
|
if (type->ty == Tarray || type->ty == Tdelegate) {
|
|
LLConstant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(1) };
|
|
value = llvm::ConstantExpr::getGetElementPtr(value, idxs, true);
|
|
}
|
|
return DtoBitCast(value, DtoType(tb));
|
|
}
|
|
else {
|
|
goto Lerr;
|
|
}
|
|
|
|
return res;
|
|
|
|
Lerr:
|
|
error("can not cast %s to %s at compile time", e1->type->toChars(), type->toChars());
|
|
if (!global.gag)
|
|
fatal();
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* SymOffExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("SymOffExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(0 && "SymOffExp::toElem should no longer be called :/");
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AddrExp::toElem(IRState* p)
|
|
{
|
|
Logger::println("AddrExp::toElem: %s @ %s", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
DValue* v = e1->toElem(p);
|
|
if (v->isField()) {
|
|
Logger::println("is field");
|
|
return v;
|
|
}
|
|
else if (DFuncValue* fv = v->isFunc()) {
|
|
Logger::println("is func");
|
|
//Logger::println("FuncDeclaration");
|
|
FuncDeclaration* fd = fv->func;
|
|
assert(fd);
|
|
fd->codegen(Type::sir);
|
|
return new DFuncValue(fd, fd->ir.irFunc->func);
|
|
}
|
|
else if (v->isIm()) {
|
|
Logger::println("is immediate");
|
|
return v;
|
|
}
|
|
Logger::println("is nothing special");
|
|
|
|
// we special case here, since apparently taking the address of a slice is ok
|
|
LLValue* lval;
|
|
if (v->isLVal())
|
|
lval = v->getLVal();
|
|
else
|
|
{
|
|
assert(v->isSlice());
|
|
LLValue* rval = v->getRVal();
|
|
lval = DtoRawAlloca(rval->getType(), 0, ".tmp_slice_storage");
|
|
DtoStore(rval, lval);
|
|
}
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "lval: " << *lval << '\n';
|
|
|
|
return new DImValue(type, DtoBitCast(lval, DtoType(type)));
|
|
}
|
|
|
|
LLConstant* AddrExp::toConstElem(IRState* p)
|
|
{
|
|
// FIXME: this should probably be generalized more so we don't
|
|
// need to have a case for each thing we can take the address of
|
|
|
|
// address of global variable
|
|
if (e1->op == TOKvar)
|
|
{
|
|
VarExp* vexp = static_cast<VarExp*>(e1);
|
|
|
|
// make sure 'this' isn't needed
|
|
if (vexp->var->needThis())
|
|
{
|
|
error("need 'this' to access %s", vexp->var->toChars());
|
|
fatal();
|
|
}
|
|
|
|
// global variable
|
|
if (VarDeclaration* vd = vexp->var->isVarDeclaration())
|
|
{
|
|
vd->codegen(Type::sir);
|
|
LLConstant* llc = llvm::dyn_cast<LLConstant>(vd->ir.getIrValue());
|
|
assert(llc);
|
|
return DtoBitCast(llc, DtoType(type));
|
|
}
|
|
// static function
|
|
else if (FuncDeclaration* fd = vexp->var->isFuncDeclaration())
|
|
{
|
|
fd->codegen(Type::sir);
|
|
IrFunction* irfunc = fd->ir.irFunc;
|
|
return irfunc->func;
|
|
}
|
|
// something else
|
|
else
|
|
{
|
|
// fail
|
|
goto Lerr;
|
|
}
|
|
}
|
|
// address of indexExp
|
|
else if (e1->op == TOKindex)
|
|
{
|
|
IndexExp* iexp = static_cast<IndexExp*>(e1);
|
|
|
|
// indexee must be global static array var
|
|
assert(iexp->e1->op == TOKvar);
|
|
VarExp* vexp = static_cast<VarExp*>(iexp->e1);
|
|
VarDeclaration* vd = vexp->var->isVarDeclaration();
|
|
assert(vd);
|
|
assert(vd->type->toBasetype()->ty == Tsarray);
|
|
vd->codegen(Type::sir);
|
|
assert(vd->ir.irGlobal);
|
|
|
|
// get index
|
|
LLConstant* index = iexp->e2->toConstElem(p);
|
|
assert(index->getType() == DtoSize_t());
|
|
|
|
// gep
|
|
LLConstant* idxs[2] = { DtoConstSize_t(0), index };
|
|
LLConstant *val = isaConstant(vd->ir.irGlobal->value);
|
|
val = DtoBitCast(val, DtoType(vd->type->pointerTo()));
|
|
LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(val, idxs, true);
|
|
|
|
// bitcast to requested type
|
|
assert(type->toBasetype()->ty == Tpointer);
|
|
return DtoBitCast(gep, DtoType(type));
|
|
}
|
|
else if (
|
|
e1->op == TOKstructliteral ||
|
|
e1->op == TOKslice)
|
|
{
|
|
error("non-constant expression '%s'", toChars());
|
|
fatal();
|
|
}
|
|
// not yet supported
|
|
else
|
|
{
|
|
Lerr:
|
|
error("constant expression '%s' not yet implemented", toChars());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PtrExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = e1->toElem(p)->getRVal();
|
|
}
|
|
|
|
DValue* PtrExp::toElem(IRState* p)
|
|
{
|
|
Logger::println("PtrExp::toElem: %s @ %s", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// function pointers are special
|
|
if (type->toBasetype()->ty == Tfunction)
|
|
{
|
|
assert(!cachedLvalue);
|
|
return new DImValue(type, e1->toElem(p)->getRVal());
|
|
}
|
|
|
|
// get the rvalue and return it as an lvalue
|
|
LLValue* V;
|
|
if (cachedLvalue)
|
|
{
|
|
V = cachedLvalue;
|
|
}
|
|
else
|
|
{
|
|
V = e1->toElem(p)->getRVal();
|
|
}
|
|
return new DVarValue(type, V);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DotVarExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = toElem(p)->getLVal();
|
|
}
|
|
|
|
DValue* DotVarExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("DotVarExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if (cachedLvalue)
|
|
{
|
|
Logger::println("using cached lvalue");
|
|
LLValue *V = cachedLvalue;
|
|
VarDeclaration* vd = var->isVarDeclaration();
|
|
assert(vd);
|
|
return new DVarValue(type, vd, V);
|
|
}
|
|
|
|
DValue* l = e1->toElem(p);
|
|
|
|
Type* e1type = e1->type->toBasetype();
|
|
|
|
//Logger::println("e1type=%s", e1type->toChars());
|
|
//Logger::cout() << *DtoType(e1type) << '\n';
|
|
|
|
if (VarDeclaration* vd = var->isVarDeclaration()) {
|
|
LLValue* arrptr;
|
|
// indexing struct pointer
|
|
if (e1type->ty == Tpointer) {
|
|
assert(e1type->nextOf()->ty == Tstruct);
|
|
TypeStruct* ts = static_cast<TypeStruct*>(e1type->nextOf());
|
|
arrptr = DtoIndexStruct(l->getRVal(), ts->sym, vd);
|
|
}
|
|
// indexing normal struct
|
|
else if (e1type->ty == Tstruct) {
|
|
TypeStruct* ts = static_cast<TypeStruct*>(e1type);
|
|
arrptr = DtoIndexStruct(l->getRVal(), ts->sym, vd);
|
|
}
|
|
// indexing class
|
|
else if (e1type->ty == Tclass) {
|
|
TypeClass* tc = static_cast<TypeClass*>(e1type);
|
|
arrptr = DtoIndexClass(l->getRVal(), tc->sym, vd);
|
|
}
|
|
else
|
|
assert(0);
|
|
|
|
//Logger::cout() << "mem: " << *arrptr << '\n';
|
|
return new DVarValue(type, vd, arrptr);
|
|
}
|
|
else if (FuncDeclaration* fdecl = var->isFuncDeclaration())
|
|
{
|
|
DtoResolveDsymbol(fdecl);
|
|
|
|
// This is a bit more convoluted than it would need to be, because it
|
|
// has to take templated interface methods into account, for which
|
|
// isFinal is not necessarily true.
|
|
const bool nonFinal = !fdecl->isFinal() &&
|
|
(fdecl->isAbstract() || fdecl->isVirtual());
|
|
|
|
// If we are calling a non-final interface function, we need to get
|
|
// the pointer to the underlying object instead of passing the
|
|
// interface pointer directly.
|
|
LLValue* passedThis = 0;
|
|
if (e1type->ty == Tclass)
|
|
{
|
|
TypeClass* tc = static_cast<TypeClass*>(e1type);
|
|
if (tc->sym->isInterfaceDeclaration() && nonFinal)
|
|
passedThis = DtoCastInterfaceToObject(l, NULL)->getRVal();
|
|
}
|
|
LLValue* vthis = l->getRVal();
|
|
if (!passedThis) passedThis = vthis;
|
|
|
|
// Decide whether this function needs to be looked up in the vtable.
|
|
// Even virtual functions are looked up directly if super or DotTypeExp
|
|
// are used, thus we need to walk through the this expression and check.
|
|
bool vtbllookup = nonFinal;
|
|
Expression* e = e1;
|
|
while (e && vtbllookup)
|
|
{
|
|
if (e->op == TOKsuper || e->op == TOKdottype)
|
|
vtbllookup = false;
|
|
else if (e->op == TOKcast)
|
|
e = static_cast<CastExp*>(e)->e1;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Get the actual function value to call.
|
|
LLValue* funcval = 0;
|
|
if (vtbllookup)
|
|
{
|
|
DImValue thisVal(e1type, vthis);
|
|
funcval = DtoVirtualFunctionPointer(&thisVal, fdecl, toChars());
|
|
}
|
|
else
|
|
{
|
|
fdecl->codegen(Type::sir);
|
|
funcval = fdecl->ir.irFunc->func;
|
|
}
|
|
assert(funcval);
|
|
|
|
return new DFuncValue(fdecl, funcval, passedThis);
|
|
}
|
|
else {
|
|
printf("unsupported dotvarexp: %s\n", var->toChars());
|
|
}
|
|
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ThisExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ThisExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// regular this expr
|
|
if (VarDeclaration* vd = var->isVarDeclaration()) {
|
|
LLValue* v;
|
|
Dsymbol* vdparent = vd->toParent2();
|
|
Identifier *ident = p->func()->decl->ident;
|
|
if (ident == Id::ensure || ident == Id::require) {
|
|
Logger::println("contract this exp");
|
|
v = p->func()->nestArg;
|
|
v = DtoBitCast(v, DtoType(type)->getPointerTo());
|
|
} else if (vdparent != p->func()->decl) {
|
|
Logger::println("nested this exp");
|
|
#if STRUCTTHISREF
|
|
return DtoNestedVariable(loc, type, vd, type->ty == Tstruct);
|
|
#else
|
|
return DtoNestedVariable(loc, type, vd);
|
|
#endif
|
|
}
|
|
else {
|
|
Logger::println("normal this exp");
|
|
v = p->func()->thisArg;
|
|
}
|
|
return new DVarValue(type, vd, v);
|
|
}
|
|
|
|
// anything we're not yet handling ?
|
|
assert(0 && "no var in ThisExp");
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void IndexExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = toElem(p)->getLVal();
|
|
}
|
|
|
|
DValue* IndexExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("IndexExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if (cachedLvalue)
|
|
{
|
|
LLValue* V = cachedLvalue;
|
|
return new DVarValue(type, V);
|
|
}
|
|
|
|
DValue* l = e1->toElem(p);
|
|
|
|
Type* e1type = e1->type->toBasetype();
|
|
|
|
p->arrays.push_back(l); // if $ is used it must be an array so this is fine.
|
|
DValue* r = e2->toElem(p);
|
|
p->arrays.pop_back();
|
|
|
|
LLValue* zero = DtoConstUint(0);
|
|
|
|
LLValue* arrptr = 0;
|
|
if (e1type->ty == Tpointer) {
|
|
arrptr = DtoGEP1(l->getRVal(),r->getRVal());
|
|
}
|
|
else if (e1type->ty == Tsarray) {
|
|
if(global.params.useArrayBounds)
|
|
DtoArrayBoundsCheck(loc, l, r);
|
|
arrptr = DtoGEP(l->getRVal(), zero, r->getRVal());
|
|
}
|
|
else if (e1type->ty == Tarray) {
|
|
if(global.params.useArrayBounds)
|
|
DtoArrayBoundsCheck(loc, l, r);
|
|
arrptr = DtoArrayPtr(l);
|
|
arrptr = DtoGEP1(arrptr,r->getRVal());
|
|
}
|
|
else if (e1type->ty == Taarray) {
|
|
return DtoAAIndex(loc, type, l, r, modifiable);
|
|
}
|
|
else {
|
|
Logger::println("invalid index exp! e1type: %s", e1type->toChars());
|
|
assert(0);
|
|
}
|
|
return new DVarValue(type, arrptr);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* SliceExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("SliceExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// this is the new slicing code, it's different in that a full slice will no longer retain the original pointer.
|
|
// but this was broken if there *was* no original pointer, ie. a slice of a slice...
|
|
// now all slices have *both* the 'len' and 'ptr' fields set to != null.
|
|
|
|
// value being sliced
|
|
LLValue* elen;
|
|
LLValue* eptr;
|
|
DValue* e = e1->toElem(p);
|
|
|
|
// handle pointer slicing
|
|
Type* etype = e1->type->toBasetype();
|
|
if (etype->ty == Tpointer)
|
|
{
|
|
assert(lwr);
|
|
eptr = e->getRVal();
|
|
}
|
|
// array slice
|
|
else
|
|
{
|
|
eptr = DtoArrayPtr(e);
|
|
}
|
|
|
|
// has lower bound, pointer needs adjustment
|
|
if (lwr)
|
|
{
|
|
// must have upper bound too then
|
|
assert(upr);
|
|
|
|
// get bounds (make sure $ works)
|
|
p->arrays.push_back(e);
|
|
DValue* lo = lwr->toElem(p);
|
|
DValue* up = upr->toElem(p);
|
|
p->arrays.pop_back();
|
|
LLValue* vlo = lo->getRVal();
|
|
LLValue* vup = up->getRVal();
|
|
|
|
#if DMDV2
|
|
if(global.params.useArrayBounds)
|
|
#else
|
|
if(global.params.useArrayBounds && (etype->ty == Tsarray || etype->ty == Tarray))
|
|
#endif
|
|
DtoArrayBoundsCheck(loc, e, up, lo);
|
|
|
|
// offset by lower
|
|
eptr = DtoGEP1(eptr, vlo);
|
|
|
|
// adjust length
|
|
elen = p->ir->CreateSub(vup, vlo, "tmp");
|
|
}
|
|
// no bounds or full slice -> just convert to slice
|
|
else
|
|
{
|
|
assert(e1->type->toBasetype()->ty != Tpointer);
|
|
// if the sliceee is a static array, we use the length of that as DMD seems
|
|
// to give contrary inconsistent sizesin some multidimensional static array cases.
|
|
// (namely default initialization, int[16][16] arr; -> int[256] arr = 0;)
|
|
if (etype->ty == Tsarray)
|
|
{
|
|
TypeSArray* tsa = static_cast<TypeSArray*>(etype);
|
|
elen = DtoConstSize_t(tsa->dim->toUInteger());
|
|
|
|
// in this case, we also need to make sure the pointer is cast to the innermost element type
|
|
eptr = DtoBitCast(eptr, DtoType(tsa->nextOf()->pointerTo()));
|
|
}
|
|
// for normal code the actual array length is what we want!
|
|
else
|
|
{
|
|
elen = DtoArrayLen(e);
|
|
}
|
|
}
|
|
|
|
return new DSliceValue(type, elen, eptr);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CmpExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CmpExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
|
|
Type* t = e1->type->toBasetype();
|
|
|
|
LLValue* eval = 0;
|
|
|
|
if (t->isintegral() || t->ty == Tpointer || t->ty == Tnull)
|
|
{
|
|
llvm::ICmpInst::Predicate cmpop;
|
|
bool skip = false;
|
|
// pointers don't report as being unsigned
|
|
bool uns = (t->isunsigned() || t->ty == Tpointer);
|
|
switch(op)
|
|
{
|
|
case TOKlt:
|
|
case TOKul:
|
|
cmpop = uns ? llvm::ICmpInst::ICMP_ULT : llvm::ICmpInst::ICMP_SLT;
|
|
break;
|
|
case TOKle:
|
|
case TOKule:
|
|
cmpop = uns ? llvm::ICmpInst::ICMP_ULE : llvm::ICmpInst::ICMP_SLE;
|
|
break;
|
|
case TOKgt:
|
|
case TOKug:
|
|
cmpop = uns ? llvm::ICmpInst::ICMP_UGT : llvm::ICmpInst::ICMP_SGT;
|
|
break;
|
|
case TOKge:
|
|
case TOKuge:
|
|
cmpop = uns ? llvm::ICmpInst::ICMP_UGE : llvm::ICmpInst::ICMP_SGE;
|
|
break;
|
|
case TOKue:
|
|
cmpop = llvm::ICmpInst::ICMP_EQ;
|
|
break;
|
|
case TOKlg:
|
|
cmpop = llvm::ICmpInst::ICMP_NE;
|
|
break;
|
|
case TOKleg:
|
|
skip = true;
|
|
eval = LLConstantInt::getTrue(gIR->context());
|
|
break;
|
|
case TOKunord:
|
|
skip = true;
|
|
eval = LLConstantInt::getFalse(gIR->context());
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (!skip)
|
|
{
|
|
LLValue* a = l->getRVal();
|
|
LLValue* b = r->getRVal();
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "type 1: " << *a << '\n';
|
|
Logger::cout() << "type 2: " << *b << '\n';
|
|
}
|
|
if (a->getType() != b->getType())
|
|
b = DtoBitCast(b, a->getType());
|
|
eval = p->ir->CreateICmp(cmpop, a, b, "tmp");
|
|
}
|
|
}
|
|
else if (t->isfloating())
|
|
{
|
|
llvm::FCmpInst::Predicate cmpop;
|
|
switch(op)
|
|
{
|
|
case TOKlt:
|
|
cmpop = llvm::FCmpInst::FCMP_OLT;break;
|
|
case TOKle:
|
|
cmpop = llvm::FCmpInst::FCMP_OLE;break;
|
|
case TOKgt:
|
|
cmpop = llvm::FCmpInst::FCMP_OGT;break;
|
|
case TOKge:
|
|
cmpop = llvm::FCmpInst::FCMP_OGE;break;
|
|
case TOKunord:
|
|
cmpop = llvm::FCmpInst::FCMP_UNO;break;
|
|
case TOKule:
|
|
cmpop = llvm::FCmpInst::FCMP_ULE;break;
|
|
case TOKul:
|
|
cmpop = llvm::FCmpInst::FCMP_ULT;break;
|
|
case TOKuge:
|
|
cmpop = llvm::FCmpInst::FCMP_UGE;break;
|
|
case TOKug:
|
|
cmpop = llvm::FCmpInst::FCMP_UGT;break;
|
|
case TOKue:
|
|
cmpop = llvm::FCmpInst::FCMP_UEQ;break;
|
|
case TOKlg:
|
|
cmpop = llvm::FCmpInst::FCMP_ONE;break;
|
|
case TOKleg:
|
|
cmpop = llvm::FCmpInst::FCMP_ORD;break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
eval = p->ir->CreateFCmp(cmpop, l->getRVal(), r->getRVal(), "tmp");
|
|
}
|
|
else if (t->ty == Tsarray || t->ty == Tarray)
|
|
{
|
|
Logger::println("static or dynamic array");
|
|
eval = DtoArrayCompare(loc,op,l,r);
|
|
}
|
|
else if (t->ty == Taarray)
|
|
{
|
|
eval = LLConstantInt::getFalse(gIR->context());
|
|
}
|
|
else
|
|
{
|
|
assert(0 && "Unsupported CmpExp type");
|
|
}
|
|
|
|
return new DImValue(type, eval);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* EqualExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("EqualExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
LLValue* lv = l->getRVal();
|
|
LLValue* rv = r->getRVal();
|
|
|
|
Type* t = e1->type->toBasetype();
|
|
|
|
LLValue* eval = 0;
|
|
|
|
// the Tclass catches interface comparisons, regular
|
|
// class equality should be rewritten as a.opEquals(b) by this time
|
|
if (t->isintegral() || t->ty == Tpointer || t->ty == Tclass || t->ty == Tnull)
|
|
{
|
|
Logger::println("integral or pointer or interface");
|
|
llvm::ICmpInst::Predicate cmpop;
|
|
switch(op)
|
|
{
|
|
case TOKequal:
|
|
cmpop = llvm::ICmpInst::ICMP_EQ;
|
|
break;
|
|
case TOKnotequal:
|
|
cmpop = llvm::ICmpInst::ICMP_NE;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (rv->getType() != lv->getType()) {
|
|
rv = DtoBitCast(rv, lv->getType());
|
|
}
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "lv: " << *lv << '\n';
|
|
Logger::cout() << "rv: " << *rv << '\n';
|
|
}
|
|
eval = p->ir->CreateICmp(cmpop, lv, rv, "tmp");
|
|
}
|
|
else if (t->isfloating()) // includes iscomplex
|
|
{
|
|
eval = DtoBinNumericEquals(loc, l, r, op);
|
|
}
|
|
else if (t->ty == Tsarray || t->ty == Tarray)
|
|
{
|
|
Logger::println("static or dynamic array");
|
|
eval = DtoArrayEquals(loc,op,l,r);
|
|
}
|
|
else if (t->ty == Taarray)
|
|
{
|
|
Logger::println("associative array");
|
|
eval = DtoAAEquals(loc,op,l,r);
|
|
}
|
|
else if (t->ty == Tdelegate)
|
|
{
|
|
Logger::println("delegate");
|
|
eval = DtoDelegateEquals(op,l->getRVal(),r->getRVal());
|
|
}
|
|
else if (t->ty == Tstruct)
|
|
{
|
|
Logger::println("struct");
|
|
// when this is reached it means there is no opEquals overload.
|
|
eval = DtoStructEquals(op,l,r);
|
|
}
|
|
else
|
|
{
|
|
assert(0 && "Unsupported EqualExp type");
|
|
}
|
|
|
|
return new DImValue(type, eval);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* PostExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("PostExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
e2->toElem(p);
|
|
|
|
LLValue* val = l->getRVal();
|
|
LLValue* post = 0;
|
|
|
|
Type* e1type = e1->type->toBasetype();
|
|
Type* e2type = e2->type->toBasetype();
|
|
|
|
if (e1type->isintegral())
|
|
{
|
|
assert(e2type->isintegral());
|
|
LLValue* one = LLConstantInt::get(val->getType(), 1, !e2type->isunsigned());
|
|
if (op == TOKplusplus) {
|
|
post = llvm::BinaryOperator::CreateAdd(val,one,"tmp",p->scopebb());
|
|
}
|
|
else if (op == TOKminusminus) {
|
|
post = llvm::BinaryOperator::CreateSub(val,one,"tmp",p->scopebb());
|
|
}
|
|
}
|
|
else if (e1type->ty == Tpointer)
|
|
{
|
|
assert(e2->op == TOKint64);
|
|
LLConstant* minusone = LLConstantInt::get(DtoSize_t(),static_cast<uint64_t>(-1),true);
|
|
LLConstant* plusone = LLConstantInt::get(DtoSize_t(),static_cast<uint64_t>(1),false);
|
|
LLConstant* whichone = (op == TOKplusplus) ? plusone : minusone;
|
|
post = llvm::GetElementPtrInst::Create(val, whichone, "tmp", p->scopebb());
|
|
}
|
|
else if (e1type->isfloating())
|
|
{
|
|
assert(e2type->isfloating());
|
|
LLValue* one = DtoConstFP(e1type, ldouble(1.0));
|
|
if (op == TOKplusplus) {
|
|
post = llvm::BinaryOperator::CreateFAdd(val,one,"tmp",p->scopebb());
|
|
}
|
|
else if (op == TOKminusminus) {
|
|
post = llvm::BinaryOperator::CreateFSub(val,one,"tmp",p->scopebb());
|
|
}
|
|
}
|
|
else
|
|
assert(post);
|
|
|
|
DtoStore(post,l->getLVal());
|
|
|
|
return new DImValue(type,val);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* NewExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("NewExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(newtype);
|
|
Type* ntype = newtype->toBasetype();
|
|
|
|
// new class
|
|
if (ntype->ty == Tclass) {
|
|
Logger::println("new class");
|
|
return DtoNewClass(loc, static_cast<TypeClass*>(ntype), this);
|
|
}
|
|
// new dynamic array
|
|
else if (ntype->ty == Tarray)
|
|
{
|
|
Logger::println("new dynamic array: %s", newtype->toChars());
|
|
// get dim
|
|
assert(arguments);
|
|
assert(arguments->dim >= 1);
|
|
if (arguments->dim == 1)
|
|
{
|
|
DValue* sz = static_cast<Expression*>(arguments->data[0])->toElem(p);
|
|
// allocate & init
|
|
return DtoNewDynArray(loc, newtype, sz, true);
|
|
}
|
|
else
|
|
{
|
|
size_t ndims = arguments->dim;
|
|
std::vector<DValue*> dims(ndims);
|
|
for (size_t i=0; i<ndims; ++i)
|
|
dims[i] = static_cast<Expression*>(arguments->data[i])->toElem(p);
|
|
return DtoNewMulDimDynArray(loc, newtype, &dims[0], ndims, true);
|
|
}
|
|
}
|
|
// new static array
|
|
else if (ntype->ty == Tsarray)
|
|
{
|
|
assert(0);
|
|
}
|
|
// new struct
|
|
else if (ntype->ty == Tstruct)
|
|
{
|
|
Logger::println("new struct on heap: %s\n", newtype->toChars());
|
|
// allocate
|
|
LLValue* mem = 0;
|
|
#if DMDV2
|
|
if (allocator)
|
|
{
|
|
// custom allocator
|
|
allocator->codegen(Type::sir);
|
|
DFuncValue dfn(allocator, allocator->ir.irFunc->func);
|
|
DValue* res = DtoCallFunction(loc, NULL, &dfn, newargs);
|
|
mem = DtoBitCast(res->getRVal(), DtoType(ntype->pointerTo()), ".newstruct_custom");
|
|
} else
|
|
#endif
|
|
{
|
|
// default allocator
|
|
mem = DtoNew(newtype);
|
|
}
|
|
// init
|
|
TypeStruct* ts = static_cast<TypeStruct*>(ntype);
|
|
if (ts->isZeroInit(ts->sym->loc)) {
|
|
DtoAggrZeroInit(mem);
|
|
}
|
|
else {
|
|
assert(ts->sym);
|
|
ts->sym->codegen(Type::sir);
|
|
DtoAggrCopy(mem, ts->sym->ir.irStruct->getInitSymbol());
|
|
}
|
|
#if DMDV2
|
|
if (ts->sym->isNested() && ts->sym->vthis)
|
|
DtoResolveNestedContext(loc, ts->sym, mem);
|
|
|
|
// call constructor
|
|
if (member)
|
|
{
|
|
Logger::println("Calling constructor");
|
|
assert(arguments != NULL);
|
|
member->codegen(Type::sir);
|
|
DFuncValue dfn(member, member->ir.irFunc->func, mem);
|
|
return DtoCallFunction(loc, ts, &dfn, arguments);
|
|
}
|
|
#endif
|
|
return new DImValue(type, mem);
|
|
}
|
|
// new basic type
|
|
else
|
|
{
|
|
// allocate
|
|
LLValue* mem = DtoNew(newtype);
|
|
DVarValue tmpvar(newtype, mem);
|
|
|
|
// default initialize
|
|
// static arrays never appear here, so using the defaultInit is ok!
|
|
Expression* exp = newtype->defaultInit(loc);
|
|
DValue* iv = exp->toElem(gIR);
|
|
DtoAssign(loc, &tmpvar, iv);
|
|
|
|
// return as pointer-to
|
|
return new DImValue(type, mem);
|
|
}
|
|
|
|
assert(0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DeleteExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("DeleteExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* dval = e1->toElem(p);
|
|
Type* et = e1->type->toBasetype();
|
|
|
|
// simple pointer
|
|
if (et->ty == Tpointer)
|
|
{
|
|
#if DMDV2
|
|
DtoDeleteMemory(dval->isLVal() ? dval->getLVal() : makeLValue(loc, dval));
|
|
#else
|
|
LLValue* rval = dval->getRVal();
|
|
DtoDeleteMemory(rval);
|
|
if (dval->isVar())
|
|
DtoStore(LLConstant::getNullValue(rval->getType()), dval->getLVal());
|
|
#endif
|
|
}
|
|
// class
|
|
else if (et->ty == Tclass)
|
|
{
|
|
bool onstack = false;
|
|
TypeClass* tc = static_cast<TypeClass*>(et);
|
|
if (tc->sym->isInterfaceDeclaration())
|
|
{
|
|
#if DMDV2
|
|
LLValue *val = dval->getLVal();
|
|
#else
|
|
LLValue *val = dval->getRVal();
|
|
#endif
|
|
DtoDeleteInterface(val);
|
|
onstack = true;
|
|
}
|
|
else if (DVarValue* vv = dval->isVar()) {
|
|
if (vv->var && vv->var->onstack) {
|
|
DtoFinalizeClass(dval->getRVal());
|
|
onstack = true;
|
|
}
|
|
}
|
|
if (!onstack) {
|
|
LLValue* rval = dval->getRVal();
|
|
DtoDeleteClass(rval);
|
|
}
|
|
if (dval->isVar()) {
|
|
LLValue* lval = dval->getLVal();
|
|
DtoStore(LLConstant::getNullValue(lval->getType()->getContainedType(0)), lval);
|
|
}
|
|
}
|
|
// dyn array
|
|
else if (et->ty == Tarray)
|
|
{
|
|
DtoDeleteArray(dval);
|
|
if (dval->isLVal())
|
|
DtoSetArrayToNull(dval->getLVal());
|
|
}
|
|
// unknown/invalid
|
|
else
|
|
{
|
|
assert(0 && "invalid delete");
|
|
}
|
|
|
|
// no value to return
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ArrayLengthExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ArrayLengthExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* u = e1->toElem(p);
|
|
return new DImValue(type, DtoArrayLen(u));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AssertExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("AssertExp::toElem: %s\n", toChars());
|
|
LOG_SCOPE;
|
|
|
|
if(!global.params.useAssert)
|
|
return NULL;
|
|
|
|
// condition
|
|
DValue* cond;
|
|
Type* condty;
|
|
|
|
// special case for dmd generated assert(this); when not in -release mode
|
|
if (e1->op == TOKthis && static_cast<ThisExp*>(e1)->var == NULL)
|
|
{
|
|
LLValue* thisarg = p->func()->thisArg;
|
|
assert(thisarg && "null thisarg, but we're in assert(this) exp;");
|
|
LLValue* thisptr = DtoLoad(p->func()->thisArg);
|
|
condty = e1->type->toBasetype();
|
|
cond = new DImValue(condty, thisptr);
|
|
}
|
|
else
|
|
{
|
|
cond = e1->toElem(p);
|
|
condty = e1->type->toBasetype();
|
|
}
|
|
|
|
// create basic blocks
|
|
llvm::BasicBlock* oldend = p->scopeend();
|
|
llvm::BasicBlock* assertbb = llvm::BasicBlock::Create(gIR->context(), "assert", p->topfunc(), oldend);
|
|
llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "noassert", p->topfunc(), oldend);
|
|
|
|
// test condition
|
|
LLValue* condval = DtoCast(loc, cond, Type::tbool)->getRVal();
|
|
|
|
// branch
|
|
llvm::BranchInst::Create(endbb, assertbb, condval, p->scopebb());
|
|
|
|
// call assert runtime functions
|
|
p->scope() = IRScope(assertbb,endbb);
|
|
DtoAssert(p->func()->decl->getModule(), loc, msg ? msg->toElem(p) : NULL);
|
|
|
|
// rewrite the scope
|
|
p->scope() = IRScope(endbb,oldend);
|
|
|
|
|
|
InvariantDeclaration* invdecl;
|
|
// class invariants
|
|
if(
|
|
global.params.useInvariants &&
|
|
condty->ty == Tclass &&
|
|
!(static_cast<TypeClass*>(condty)->sym->isInterfaceDeclaration()))
|
|
{
|
|
Logger::println("calling class invariant");
|
|
llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_invariant");
|
|
LLValue* arg = DtoBitCast(cond->getRVal(), fn->getFunctionType()->getParamType(0));
|
|
gIR->CreateCallOrInvoke(fn, arg);
|
|
}
|
|
// struct invariants
|
|
else if(
|
|
global.params.useInvariants &&
|
|
condty->ty == Tpointer && condty->nextOf()->ty == Tstruct &&
|
|
(invdecl = static_cast<TypeStruct*>(condty->nextOf())->sym->inv) != NULL)
|
|
{
|
|
Logger::print("calling struct invariant");
|
|
static_cast<TypeStruct*>(condty->nextOf())->sym->codegen(Type::sir);
|
|
DFuncValue invfunc(invdecl, invdecl->ir.irFunc->func, cond->getRVal());
|
|
DtoCallFunction(loc, NULL, &invfunc, NULL);
|
|
}
|
|
|
|
// DMD allows syntax like this:
|
|
// f() == 0 || assert(false)
|
|
return new DImValue(type, DtoConstBool(false));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* NotExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("NotExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* u = e1->toElem(p);
|
|
|
|
LLValue* b = DtoCast(loc, u, Type::tbool)->getRVal();
|
|
|
|
LLConstant* zero = DtoConstBool(false);
|
|
b = p->ir->CreateICmpEQ(b,zero);
|
|
|
|
return new DImValue(type, b);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AndAndExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("AndAndExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* u = e1->toElem(p);
|
|
|
|
llvm::BasicBlock* oldend = p->scopeend();
|
|
llvm::BasicBlock* andand = llvm::BasicBlock::Create(gIR->context(), "andand", gIR->topfunc(), oldend);
|
|
llvm::BasicBlock* andandend = llvm::BasicBlock::Create(gIR->context(), "andandend", gIR->topfunc(), oldend);
|
|
|
|
LLValue* ubool = DtoCast(loc, u, Type::tbool)->getRVal();
|
|
|
|
llvm::BasicBlock* oldblock = p->scopebb();
|
|
llvm::BranchInst::Create(andand,andandend,ubool,p->scopebb());
|
|
|
|
p->scope() = IRScope(andand, andandend);
|
|
DValue* v = e2->toElemDtor(p);
|
|
|
|
LLValue* vbool = 0;
|
|
if (!v->isFunc() && v->getType() != Type::tvoid)
|
|
{
|
|
vbool = DtoCast(loc, v, Type::tbool)->getRVal();
|
|
}
|
|
|
|
llvm::BasicBlock* newblock = p->scopebb();
|
|
llvm::BranchInst::Create(andandend,p->scopebb());
|
|
p->scope() = IRScope(andandend, oldend);
|
|
|
|
LLValue* resval = 0;
|
|
if (ubool == vbool || !vbool) {
|
|
// No need to create a PHI node.
|
|
resval = ubool;
|
|
} else {
|
|
llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "andandval");
|
|
// If we jumped over evaluation of the right-hand side,
|
|
// the result is false. Otherwise it's the value of the right-hand side.
|
|
phi->addIncoming(LLConstantInt::getFalse(gIR->context()), oldblock);
|
|
phi->addIncoming(vbool, newblock);
|
|
resval = phi;
|
|
}
|
|
|
|
return new DImValue(type, resval);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* OrOrExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("OrOrExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* u = e1->toElem(p);
|
|
|
|
llvm::BasicBlock* oldend = p->scopeend();
|
|
llvm::BasicBlock* oror = llvm::BasicBlock::Create(gIR->context(), "oror", gIR->topfunc(), oldend);
|
|
llvm::BasicBlock* ororend = llvm::BasicBlock::Create(gIR->context(), "ororend", gIR->topfunc(), oldend);
|
|
|
|
LLValue* ubool = DtoCast(loc, u, Type::tbool)->getRVal();
|
|
|
|
llvm::BasicBlock* oldblock = p->scopebb();
|
|
llvm::BranchInst::Create(ororend,oror,ubool,p->scopebb());
|
|
|
|
p->scope() = IRScope(oror, ororend);
|
|
DValue* v = e2->toElemDtor(p);
|
|
|
|
LLValue* vbool = 0;
|
|
if (v && !v->isFunc() && v->getType() != Type::tvoid)
|
|
{
|
|
vbool = DtoCast(loc, v, Type::tbool)->getRVal();
|
|
}
|
|
|
|
llvm::BasicBlock* newblock = p->scopebb();
|
|
llvm::BranchInst::Create(ororend,p->scopebb());
|
|
p->scope() = IRScope(ororend, oldend);
|
|
|
|
LLValue* resval = 0;
|
|
if (ubool == vbool || !vbool) {
|
|
// No need to create a PHI node.
|
|
resval = ubool;
|
|
} else {
|
|
llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "ororval");
|
|
// If we jumped over evaluation of the right-hand side,
|
|
// the result is true. Otherwise, it's the value of the right-hand side.
|
|
phi->addIncoming(LLConstantInt::getTrue(gIR->context()), oldblock);
|
|
phi->addIncoming(vbool, newblock);
|
|
resval = phi;
|
|
}
|
|
|
|
return new DImValue(type, resval);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define BinBitExp(X,Y) \
|
|
DValue* X##Exp::toElem(IRState* p) \
|
|
{ \
|
|
Logger::print("%sExp::toElem: %s @ %s\n", #X, toChars(), type->toChars()); \
|
|
LOG_SCOPE; \
|
|
DValue* u = e1->toElem(p); \
|
|
DValue* v = e2->toElem(p); \
|
|
errorOnIllegalArrayOp(this, e1, e2); \
|
|
LLValue* x = llvm::BinaryOperator::Create(llvm::Instruction::Y, u->getRVal(), v->getRVal(), "tmp", p->scopebb()); \
|
|
return new DImValue(type, x); \
|
|
}
|
|
|
|
BinBitExp(And,And);
|
|
BinBitExp(Or,Or);
|
|
BinBitExp(Xor,Xor);
|
|
BinBitExp(Shl,Shl);
|
|
BinBitExp(Ushr,LShr);
|
|
|
|
DValue* ShrExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ShrExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
DValue* u = e1->toElem(p);
|
|
DValue* v = e2->toElem(p);
|
|
LLValue* x;
|
|
if (e1->type->isunsigned())
|
|
x = p->ir->CreateLShr(u->getRVal(), v->getRVal(), "tmp");
|
|
else
|
|
x = p->ir->CreateAShr(u->getRVal(), v->getRVal(), "tmp");
|
|
return new DImValue(type, x);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* HaltExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("HaltExp::toElem: %s\n", toChars());
|
|
LOG_SCOPE;
|
|
|
|
// FIXME: DMD inserts a trap here... we probably should as well !?!
|
|
|
|
#if 1
|
|
DtoAssert(p->func()->decl->getModule(), loc, NULL);
|
|
#else
|
|
// call the new (?) trap intrinsic
|
|
p->ir->CreateCall(GET_INTRINSIC_DECL(trap),"");
|
|
new llvm::UnreachableInst(p->scopebb());
|
|
#endif
|
|
|
|
// this terminated the basicblock, start a new one
|
|
// this is sensible, since someone might goto behind the assert
|
|
// and prevents compiler errors if a terminator follows the assert
|
|
llvm::BasicBlock* oldend = gIR->scopeend();
|
|
llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterhalt", p->topfunc(), oldend);
|
|
p->scope() = IRScope(bb,oldend);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DelegateExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("DelegateExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if(func->isStatic())
|
|
error("can't take delegate of static function %s, it does not require a context ptr", func->toChars());
|
|
|
|
LLPointerType* int8ptrty = getPtrToType(LLType::getInt8Ty(gIR->context()));
|
|
|
|
assert(type->toBasetype()->ty == Tdelegate);
|
|
LLType* dgty = DtoType(type);
|
|
|
|
DValue* u = e1->toElem(p);
|
|
LLValue* uval;
|
|
if (DFuncValue* f = u->isFunc()) {
|
|
assert(f->func);
|
|
LLValue* contextptr = DtoNestedContext(loc, f->func);
|
|
uval = DtoBitCast(contextptr, getVoidPtrType());
|
|
}
|
|
else {
|
|
DValue* src = u;
|
|
if (ClassDeclaration* cd = u->getType()->isClassHandle())
|
|
{
|
|
Logger::println("context type is class handle");
|
|
if (cd->isInterfaceDeclaration())
|
|
{
|
|
Logger::println("context type is interface");
|
|
src = DtoCastInterfaceToObject(u, ClassDeclaration::object->type);
|
|
}
|
|
}
|
|
uval = src->getRVal();
|
|
}
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "context = " << *uval << '\n';
|
|
|
|
LLValue* castcontext = DtoBitCast(uval, int8ptrty);
|
|
|
|
Logger::println("func: '%s'", func->toPrettyChars());
|
|
|
|
LLValue* castfptr;
|
|
|
|
if (e1->op != TOKsuper && e1->op != TOKdottype && func->isVirtual() && !func->isFinal())
|
|
castfptr = DtoVirtualFunctionPointer(u, func, toChars());
|
|
else if (func->isAbstract())
|
|
assert(0 && "TODO delegate to abstract method");
|
|
else if (func->toParent()->isInterfaceDeclaration())
|
|
assert(0 && "TODO delegate to interface method");
|
|
else
|
|
{
|
|
func->codegen(Type::sir);
|
|
castfptr = func->ir.irFunc->func;
|
|
}
|
|
|
|
castfptr = DtoBitCast(castfptr, dgty->getContainedType(1));
|
|
|
|
return new DImValue(type, DtoAggrPair(DtoType(type), castcontext, castfptr, ".dg"));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* IdentityExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("IdentityExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
DValue* r = e2->toElem(p);
|
|
LLValue* lv = l->getRVal();
|
|
LLValue* rv = r->getRVal();
|
|
|
|
Type* t1 = e1->type->toBasetype();
|
|
|
|
// handle dynarray specially
|
|
if (t1->ty == Tarray)
|
|
return new DImValue(type, DtoDynArrayIs(op,l,r));
|
|
// also structs
|
|
else if (t1->ty == Tstruct)
|
|
return new DImValue(type, DtoStructEquals(op,l,r));
|
|
|
|
// FIXME this stuff isn't pretty
|
|
LLValue* eval = 0;
|
|
|
|
if (t1->ty == Tdelegate) {
|
|
if (r->isNull()) {
|
|
rv = NULL;
|
|
}
|
|
else {
|
|
assert(lv->getType() == rv->getType());
|
|
}
|
|
eval = DtoDelegateEquals(op,lv,rv);
|
|
}
|
|
else if (t1->isfloating()) // includes iscomplex
|
|
{
|
|
eval = DtoBinNumericEquals(loc, l, r, op);
|
|
}
|
|
else if (t1->ty == Tpointer || t1->ty == Tclass)
|
|
{
|
|
if (lv->getType() != rv->getType()) {
|
|
if (r->isNull())
|
|
rv = llvm::ConstantPointerNull::get(isaPointer(lv->getType()));
|
|
else
|
|
rv = DtoBitCast(rv, lv->getType());
|
|
}
|
|
eval = (op == TOKidentity)
|
|
? p->ir->CreateICmpEQ(lv,rv,"tmp")
|
|
: p->ir->CreateICmpNE(lv,rv,"tmp");
|
|
}
|
|
else {
|
|
assert(lv->getType() == rv->getType());
|
|
eval = (op == TOKidentity)
|
|
? p->ir->CreateICmpEQ(lv,rv,"tmp")
|
|
: p->ir->CreateICmpNE(lv,rv,"tmp");
|
|
}
|
|
return new DImValue(type, eval);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CommaExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CommaExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
if (cachedLvalue)
|
|
{
|
|
LLValue* V = cachedLvalue;
|
|
return new DVarValue(type, V);
|
|
}
|
|
|
|
e1->toElem(p);
|
|
DValue* v = e2->toElem(p);
|
|
assert(e2->type == type);
|
|
return v;
|
|
}
|
|
|
|
void CommaExp::cacheLvalue(IRState* p)
|
|
{
|
|
Logger::println("Caching l-value of %s", toChars());
|
|
LOG_SCOPE;
|
|
cachedLvalue = toElem(p)->getLVal();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CondExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CondExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
Type* dtype = type->toBasetype();
|
|
|
|
DValue* dvv;
|
|
// voids returns will need no storage
|
|
if (dtype->ty != Tvoid) {
|
|
// allocate a temporary for the final result. failed to come up with a better way :/
|
|
LLValue* resval = DtoAlloca(dtype,"condtmp");
|
|
dvv = new DVarValue(type, resval);
|
|
} else {
|
|
dvv = new DConstValue(type, getNullValue(DtoTypeNotVoid(dtype)));
|
|
}
|
|
|
|
llvm::BasicBlock* oldend = p->scopeend();
|
|
llvm::BasicBlock* condtrue = llvm::BasicBlock::Create(gIR->context(), "condtrue", gIR->topfunc(), oldend);
|
|
llvm::BasicBlock* condfalse = llvm::BasicBlock::Create(gIR->context(), "condfalse", gIR->topfunc(), oldend);
|
|
llvm::BasicBlock* condend = llvm::BasicBlock::Create(gIR->context(), "condend", gIR->topfunc(), oldend);
|
|
|
|
DValue* c = econd->toElem(p);
|
|
LLValue* cond_val = DtoCast(loc, c, Type::tbool)->getRVal();
|
|
llvm::BranchInst::Create(condtrue,condfalse,cond_val,p->scopebb());
|
|
|
|
p->scope() = IRScope(condtrue, condfalse);
|
|
DValue* u = e1->toElemDtor(p);
|
|
if (dtype->ty != Tvoid)
|
|
DtoAssign(loc, dvv, u);
|
|
llvm::BranchInst::Create(condend,p->scopebb());
|
|
|
|
p->scope() = IRScope(condfalse, condend);
|
|
DValue* v = e2->toElemDtor(p);
|
|
if (dtype->ty != Tvoid)
|
|
DtoAssign(loc, dvv, v);
|
|
llvm::BranchInst::Create(condend,p->scopebb());
|
|
|
|
p->scope() = IRScope(condend, oldend);
|
|
return dvv;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ComExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ComExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* u = e1->toElem(p);
|
|
|
|
LLValue* value = u->getRVal();
|
|
LLValue* minusone = LLConstantInt::get(value->getType(), static_cast<uint64_t>(-1), true);
|
|
value = llvm::BinaryOperator::Create(llvm::Instruction::Xor, value, minusone, "tmp", p->scopebb());
|
|
|
|
return new DImValue(type, value);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* NegExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("NegExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
|
|
if (type->iscomplex()) {
|
|
return DtoComplexNeg(loc, type, l);
|
|
}
|
|
|
|
LLValue* val = l->getRVal();
|
|
|
|
if (type->isintegral())
|
|
val = gIR->ir->CreateNeg(val,"negval");
|
|
else
|
|
val = gIR->ir->CreateFNeg(val,"negval");
|
|
|
|
return new DImValue(type, val);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CatExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CatExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
#if DMDV2
|
|
return DtoCatArrays(type, e1, e2);
|
|
#else
|
|
|
|
bool arrNarr = e1->type->toBasetype() == e2->type->toBasetype();
|
|
// array ~ array
|
|
if (arrNarr)
|
|
{
|
|
return DtoCatArrays(type, e1, e2);
|
|
}
|
|
// array ~ element
|
|
// element ~ array
|
|
else
|
|
{
|
|
return DtoCatArrayElement(type, e1, e2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* CatAssignExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("CatAssignExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* l = e1->toElem(p);
|
|
|
|
Type* e1type = e1->type->toBasetype();
|
|
assert(e1type->ty == Tarray);
|
|
Type* elemtype = e1type->nextOf()->toBasetype();
|
|
Type* e2type = e2->type->toBasetype();
|
|
|
|
if (e1type->ty == Tarray && e2type->ty == Tdchar &&
|
|
(elemtype->ty == Tchar || elemtype->ty == Twchar))
|
|
{
|
|
if (elemtype->ty == Tchar)
|
|
// append dchar to char[]
|
|
DtoAppendDCharToString(l, e2);
|
|
else /*if (elemtype->ty == Twchar)*/
|
|
// append dchar to wchar[]
|
|
DtoAppendDCharToUnicodeString(l, e2);
|
|
}
|
|
else if (e1type->equals(e2type)) {
|
|
// apeend array
|
|
DSliceValue* slice = DtoCatAssignArray(l,e2);
|
|
DtoAssign(loc, l, slice);
|
|
}
|
|
else {
|
|
// append element
|
|
DtoCatAssignElement(loc, e1type, l, e2);
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* FuncExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("FuncExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(fd);
|
|
|
|
if (fd->isNested()) Logger::println("nested");
|
|
Logger::println("kind = %s\n", fd->kind());
|
|
|
|
fd->codegen(Type::sir);
|
|
assert(fd->ir.irFunc->func);
|
|
|
|
if(fd->isNested() && !(fd->tok == TOKreserved && type->ty == Tpointer && fd->vthis)) {
|
|
LLType* dgty = DtoType(type);
|
|
|
|
LLValue* cval;
|
|
IrFunction* irfn = p->func();
|
|
if (irfn->nestedVar
|
|
#if DMDV2
|
|
// We cannot use a frame allocated in one function
|
|
// for a delegate created in another function
|
|
// (that happens with anonymous functions)
|
|
&& fd->toParent2() == irfn->decl
|
|
#endif
|
|
)
|
|
cval = irfn->nestedVar;
|
|
else if (irfn->nestArg)
|
|
cval = DtoLoad(irfn->nestArg);
|
|
#if DMDV2
|
|
// TODO: should we enable that for D1 as well?
|
|
else if (irfn->thisArg)
|
|
{
|
|
AggregateDeclaration* ad = irfn->decl->isMember2();
|
|
if (!ad || !ad->vthis) {
|
|
cval = getNullPtr(getVoidPtrType());
|
|
} else {
|
|
cval = ad->isClassDeclaration() ? DtoLoad(irfn->thisArg) : irfn->thisArg;
|
|
cval = DtoLoad(DtoGEPi(cval, 0,ad->vthis->ir.irField->index, ".vthis"));
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
cval = getNullPtr(getVoidPtrType());
|
|
cval = DtoBitCast(cval, dgty->getContainedType(0));
|
|
|
|
LLValue* castfptr = DtoBitCast(fd->ir.irFunc->func, dgty->getContainedType(1));
|
|
|
|
return new DImValue(type, DtoAggrPair(cval, castfptr, ".func"));
|
|
|
|
} else {
|
|
return new DImValue(type, fd->ir.irFunc->func);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* FuncExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("FuncExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(fd);
|
|
if (fd->tok != TOKfunction && !(fd->tok == TOKreserved && type->ty == Tpointer && fd->vthis))
|
|
{
|
|
assert(fd->tok == TOKdelegate || fd->tok == TOKreserved);
|
|
error("delegate literals as constant expressions are not yet allowed");
|
|
return 0;
|
|
}
|
|
|
|
fd->codegen(Type::sir);
|
|
assert(fd->ir.irFunc->func);
|
|
|
|
return fd->ir.irFunc->func;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* ArrayLiteralExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("ArrayLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// D types
|
|
Type* arrayType = type->toBasetype();
|
|
Type* elemType = arrayType->nextOf()->toBasetype();
|
|
|
|
// is dynamic ?
|
|
bool dyn = (arrayType->ty == Tarray);
|
|
// length
|
|
size_t len = elements->dim;
|
|
|
|
// llvm target type
|
|
LLType* llType = DtoType(arrayType);
|
|
if (Logger::enabled())
|
|
Logger::cout() << (dyn?"dynamic":"static") << " array literal with length " << len << " of D type: '" << arrayType->toChars() << "' has llvm type: '" << *llType << "'\n";
|
|
|
|
// llvm storage type
|
|
LLType* llElemType = DtoTypeNotVoid(elemType);
|
|
LLType* llStoType = LLArrayType::get(llElemType, len);
|
|
if (Logger::enabled())
|
|
Logger::cout() << "llvm storage type: '" << *llStoType << "'\n";
|
|
|
|
// don't allocate storage for zero length dynamic array literals
|
|
if (dyn && len == 0)
|
|
{
|
|
// dmd seems to just make them null...
|
|
return new DSliceValue(type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType)));
|
|
}
|
|
|
|
// dst pointer
|
|
LLValue* dstMem;
|
|
DSliceValue* dynSlice = NULL;
|
|
if(dyn)
|
|
{
|
|
dynSlice = DtoNewDynArray(loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false);
|
|
dstMem = dynSlice->ptr;
|
|
}
|
|
else
|
|
dstMem = DtoRawAlloca(llStoType, 0, "arrayliteral");
|
|
|
|
// store elements
|
|
for (size_t i=0; i<len; ++i)
|
|
{
|
|
Expression* expr = static_cast<Expression*>(elements->data[i]);
|
|
LLValue* elemAddr;
|
|
if(dyn)
|
|
elemAddr = DtoGEPi1(dstMem, i, "tmp", p->scopebb());
|
|
else
|
|
elemAddr = DtoGEPi(dstMem,0,i,"tmp",p->scopebb());
|
|
|
|
// emulate assignment
|
|
DVarValue* vv = new DVarValue(expr->type, elemAddr);
|
|
DValue* e = expr->toElem(p);
|
|
DtoAssign(loc, vv, e);
|
|
}
|
|
|
|
// return storage directly ?
|
|
if (!dyn)
|
|
return new DImValue(type, dstMem);
|
|
|
|
// return slice
|
|
return dynSlice;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* ArrayLiteralExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("ArrayLiteralExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// extract D types
|
|
Type* bt = type->toBasetype();
|
|
Type* elemt = bt->nextOf();
|
|
|
|
// build llvm array type
|
|
LLArrayType* arrtype = LLArrayType::get(DtoTypeNotVoid(elemt), elements->dim);
|
|
|
|
// dynamic arrays can occur here as well ...
|
|
bool dyn = (bt->ty != Tsarray);
|
|
|
|
// build the initializer
|
|
std::vector<LLConstant*> vals(elements->dim, NULL);
|
|
for (unsigned i=0; i<elements->dim; ++i)
|
|
{
|
|
Expression* expr = static_cast<Expression*>(elements->data[i]);
|
|
vals[i] = expr->toConstElem(p);
|
|
}
|
|
|
|
// build the constant array initialize
|
|
LLArrayType *t = elements->dim == 0 ?
|
|
arrtype :
|
|
LLArrayType::get(vals.front()->getType(), elements->dim);
|
|
LLConstant* initval = LLConstantArray::get(t, vals);
|
|
|
|
// if static array, we're done
|
|
if (!dyn)
|
|
return initval;
|
|
|
|
// we need to put the initializer in a global, and so we have a pointer to the array
|
|
// Important: don't make the global constant, since this const initializer might
|
|
// be used as an initializer for a static T[] - where modifying contents is allowed.
|
|
LLConstant* globalstore = new LLGlobalVariable(*gIR->module, t, false, LLGlobalValue::InternalLinkage, initval, ".dynarrayStorage");
|
|
globalstore = DtoBitCast(globalstore, getPtrToType(arrtype));
|
|
|
|
#if DMDV2
|
|
if (bt->ty == Tpointer)
|
|
// we need to return pointer to the static array.
|
|
return globalstore;
|
|
#endif
|
|
|
|
// build a constant dynamic array reference with the .ptr field pointing into globalstore
|
|
LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) };
|
|
LLConstant* globalstorePtr = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true);
|
|
|
|
return DtoConstSlice(DtoConstSize_t(elements->dim), globalstorePtr);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init);
|
|
|
|
static LLValue* write_zeroes(LLValue* mem, unsigned start, unsigned end) {
|
|
mem = DtoBitCast(mem, getVoidPtrType());
|
|
LLValue* gep = DtoGEPi1(mem, start, ".padding");
|
|
DtoMemSetZero(gep, DtoConstSize_t(end - start));
|
|
return mem;
|
|
}
|
|
|
|
DValue* StructLiteralExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("StructLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// make sure the struct is fully resolved
|
|
sd->codegen(Type::sir);
|
|
|
|
// alloca a stack slot
|
|
LLValue* mem = DtoRawAlloca(DtoType(type), 0, ".structliteral");
|
|
|
|
// ready elements data
|
|
assert(elements && "struct literal has null elements");
|
|
size_t nexprs = elements->dim;
|
|
Expression** exprs = (Expression**)elements->data;
|
|
|
|
LLValue* voidptr = mem;
|
|
unsigned offset = 0;
|
|
|
|
// go through fields
|
|
ArrayIter<VarDeclaration> it(sd->fields);
|
|
for (; !it.done(); it.next())
|
|
{
|
|
VarDeclaration* vd = it.get();
|
|
|
|
// don't re-initialize unions
|
|
if (vd->offset < offset)
|
|
{
|
|
IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
|
|
continue;
|
|
}
|
|
// initialize any padding so struct comparisons work
|
|
if (vd->offset != offset)
|
|
voidptr = write_zeroes(voidptr, offset, vd->offset);
|
|
offset = vd->offset + vd->type->size();
|
|
|
|
IF_LOG Logger::println("initializing field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
|
|
|
|
// get initializer
|
|
Expression* expr = (it.index < nexprs) ? exprs[it.index] : NULL;
|
|
IF_LOG Logger::println("expr: %p", expr);
|
|
DValue* val;
|
|
DConstValue cv(vd->type, NULL); // Only used in one branch; value is set beforehand
|
|
if (expr)
|
|
{
|
|
IF_LOG Logger::println("expr %zu = %s", it.index, expr->toChars());
|
|
val = expr->toElem(gIR);
|
|
}
|
|
#if DMDV2
|
|
else if (vd == sd->vthis) {
|
|
IF_LOG Logger::println("initializing vthis");
|
|
val = new DImValue(vd->type, DtoBitCast(DtoNestedContext(loc, sd), DtoType(vd->type)));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
if (vd->init && vd->init->isVoidInitializer())
|
|
continue;
|
|
IF_LOG Logger::println("using default initializer");
|
|
cv.c = get_default_initializer(vd, NULL);
|
|
val = &cv;
|
|
}
|
|
|
|
// get a pointer to this field
|
|
DVarValue field(vd->type, vd, DtoIndexStruct(mem, sd, vd));
|
|
|
|
// store the initializer there
|
|
DtoAssign(loc, &field, val, TOKconstruct);
|
|
|
|
#if DMDV2
|
|
Type *tb = vd->type->toBasetype();
|
|
if (tb->ty == Tstruct)
|
|
{
|
|
// Call postBlit()
|
|
StructDeclaration *sd = static_cast<TypeStruct *>(tb)->sym;
|
|
if (sd->postblit)
|
|
{
|
|
FuncDeclaration *fd = sd->postblit;
|
|
fd->codegen(Type::sir);
|
|
Expressions args;
|
|
DFuncValue dfn(fd, fd->ir.irFunc->func, val->getLVal());
|
|
DtoCallFunction(loc, Type::basic[Tvoid], &dfn, &args);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
// initialize trailing padding
|
|
if (sd->structsize != offset)
|
|
write_zeroes(voidptr, offset, sd->structsize);
|
|
|
|
// return as a var
|
|
return new DVarValue(type, mem);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant* StructLiteralExp::toConstElem(IRState* p)
|
|
{
|
|
Logger::print("StructLiteralExp::toConstElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// make sure the struct is resolved
|
|
sd->codegen(Type::sir);
|
|
|
|
// get inits
|
|
std::vector<LLValue*> inits(sd->fields.dim, NULL);
|
|
|
|
size_t nexprs = elements->dim;;
|
|
Expression** exprs = (Expression**)elements->data;
|
|
|
|
for (size_t i = 0; i < nexprs; i++)
|
|
if (exprs[i])
|
|
inits[i] = exprs[i]->toConstElem(p);
|
|
|
|
// vector of values to build aggregate from
|
|
std::vector<LLValue*> values = DtoStructLiteralValues(sd, inits, true);
|
|
|
|
// we know those values are constants.. cast them
|
|
std::vector<LLConstant*> constvals(values.size(), NULL);
|
|
std::vector<LLType*> types(values.size(), NULL);
|
|
for (size_t i = 0; i < values.size(); ++i) {
|
|
constvals[i] = llvm::cast<LLConstant>(values[i]);
|
|
types[i] = values[i]->getType();
|
|
}
|
|
|
|
// return constant struct
|
|
if (!constType)
|
|
constType = LLStructType::get(gIR->context(), types);
|
|
else if (constType->isOpaque())
|
|
constType->setBody(types);
|
|
return LLConstantStruct::get(constType, llvm::makeArrayRef(constvals));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* InExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("InExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* key = e1->toElem(p);
|
|
DValue* aa = e2->toElem(p);
|
|
|
|
return DtoAAIn(loc, type, aa, key);
|
|
}
|
|
|
|
DValue* RemoveExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("RemoveExp::toElem: %s\n", toChars());
|
|
LOG_SCOPE;
|
|
|
|
DValue* aa = e1->toElem(p);
|
|
DValue* key = e2->toElem(p);
|
|
|
|
return DtoAARemove(loc, aa, key);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* AssocArrayLiteralExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(keys);
|
|
assert(values);
|
|
assert(keys->dim == values->dim);
|
|
|
|
Type* basetype = type->toBasetype();
|
|
Type* aatype = basetype;
|
|
Type* vtype = aatype->nextOf();
|
|
|
|
#if DMDV2
|
|
if (!keys->dim)
|
|
goto LruntimeInit;
|
|
|
|
if (aatype->ty != Taarray) {
|
|
// It's the AssociativeArray type.
|
|
// Turn it back into a TypeAArray
|
|
vtype = values->tdata()[0]->type;
|
|
aatype = new TypeAArray(vtype, keys->tdata()[0]->type);
|
|
aatype = aatype->semantic(loc, NULL);
|
|
}
|
|
|
|
{
|
|
std::vector<LLConstant*> keysInits, valuesInits;
|
|
for (size_t i = 0, n = keys->dim; i < n; ++i)
|
|
{
|
|
Expression* ekey = keys->tdata()[i];
|
|
Expression* eval = values->tdata()[i];
|
|
Logger::println("(%zu) aa[%s] = %s", i, ekey->toChars(), eval->toChars());
|
|
unsigned errors = global.startGagging();
|
|
LLConstant *ekeyConst = ekey->toConstElem(p);
|
|
LLConstant *evalConst = eval->toConstElem(p);
|
|
if (global.endGagging(errors))
|
|
goto LruntimeInit;
|
|
assert(ekeyConst && evalConst);
|
|
keysInits.push_back(ekeyConst);
|
|
valuesInits.push_back(evalConst);
|
|
}
|
|
|
|
assert(aatype->ty == Taarray);
|
|
Type* indexType = static_cast<TypeAArray*>(aatype)->index;
|
|
assert(indexType && vtype);
|
|
|
|
llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_assocarrayliteralTX");
|
|
LLFunctionType* funcTy = func->getFunctionType();
|
|
LLValue* aaTypeInfo = DtoTypeInfoOf(stripModifiers(aatype));
|
|
|
|
LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) };
|
|
|
|
LLArrayType* arrtype = LLArrayType::get(DtoType(indexType), keys->dim);
|
|
LLConstant* initval = LLConstantArray::get(arrtype, keysInits);
|
|
LLConstant* globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage");
|
|
LLConstant* slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true);
|
|
slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice);
|
|
LLValue* keysArray = DtoAggrPaint(slice, funcTy->getParamType(1));
|
|
|
|
arrtype = LLArrayType::get(DtoType(vtype), values->dim);
|
|
initval = LLConstantArray::get(arrtype, valuesInits);
|
|
globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage");
|
|
slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true);
|
|
slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice);
|
|
LLValue* valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2));
|
|
|
|
LLValue* aa = gIR->CreateCallOrInvoke3(func, aaTypeInfo, keysArray, valuesArray, "aa").getInstruction();
|
|
if (basetype->ty != Taarray) {
|
|
LLValue *tmp = DtoAlloca(type, "aaliteral");
|
|
DtoStore(aa, DtoGEPi(tmp, 0, 0));
|
|
return new DVarValue(type, tmp);
|
|
} else {
|
|
return new DImValue(type, aa);
|
|
}
|
|
}
|
|
|
|
LruntimeInit:
|
|
#endif
|
|
|
|
// it should be possible to avoid the temporary in some cases
|
|
LLValue* tmp = DtoAlloca(type, "aaliteral");
|
|
DValue* aa = new DVarValue(type, tmp);
|
|
DtoStore(LLConstant::getNullValue(DtoType(type)), tmp);
|
|
|
|
const size_t n = keys->dim;
|
|
for (size_t i=0; i<n; ++i)
|
|
{
|
|
Expression* ekey = static_cast<Expression*>(keys->data[i]);
|
|
Expression* eval = static_cast<Expression*>(values->data[i]);
|
|
|
|
Logger::println("(%zu) aa[%s] = %s", i, ekey->toChars(), eval->toChars());
|
|
|
|
// index
|
|
DValue* key = ekey->toElem(p);
|
|
DValue* mem = DtoAAIndex(loc, vtype, aa, key, true);
|
|
|
|
// store
|
|
DValue* val = eval->toElem(p);
|
|
DtoAssign(loc, mem, val);
|
|
}
|
|
|
|
return aa;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* GEPExp::toElem(IRState* p)
|
|
{
|
|
// (&a.foo).funcptr is a case where e1->toElem is genuinely not an l-value.
|
|
LLValue* val = makeLValue(loc, e1->toElem(p));
|
|
LLValue* v = DtoGEPi(val, 0, index);
|
|
return new DVarValue(type, DtoBitCast(v, getPtrToType(DtoType(type))));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* BoolExp::toElem(IRState* p)
|
|
{
|
|
return new DImValue(type, DtoCast(loc, e1->toElem(p), Type::tbool)->getRVal());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DotTypeExp::toElem(IRState* p)
|
|
{
|
|
Type* t = sym->getType();
|
|
assert(t);
|
|
return e1->toElem(p);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* TypeExp::toElem(IRState *p)
|
|
{
|
|
error("type %s is not an expression", toChars());
|
|
//TODO: Improve error handling. DMD just returns some value here and hopes
|
|
// some more sensible error messages will be triggered.
|
|
fatal();
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* TupleExp::toElem(IRState *p)
|
|
{
|
|
Logger::print("TupleExp::toElem() %s\n", toChars());
|
|
std::vector<LLType*> types(exps->dim, NULL);
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{
|
|
Expression *el = static_cast<Expression *>(exps->data[i]);
|
|
types[i] = DtoTypeNotVoid(el->type);
|
|
}
|
|
LLValue *val = DtoRawAlloca(LLStructType::get(gIR->context(), types),0, "tuple");
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{
|
|
Expression *el = static_cast<Expression *>(exps->data[i]);
|
|
DValue* ep = el->toElem(p);
|
|
LLValue *gep = DtoGEPi(val,0,i);
|
|
if (el->type->ty == Tstruct)
|
|
DtoStore(DtoLoad(ep->getRVal()), gep);
|
|
else if (el->type->ty != Tvoid)
|
|
DtoStore(ep->getRVal(), gep);
|
|
else
|
|
DtoStore(LLConstantInt::get(LLType::getInt8Ty(gIR->context()), 0, false), gep);
|
|
}
|
|
return new DImValue(type, val);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if DMDV2
|
|
|
|
DValue* VectorExp::toElem(IRState* p)
|
|
{
|
|
Logger::print("VectorExp::toElem() %s\n", toChars());
|
|
|
|
TypeVector *type = static_cast<TypeVector*>(to->toBasetype());
|
|
assert(type->ty == Tvector);
|
|
|
|
DValue *val = e1->toElem(p);
|
|
val = DtoCast(loc, val, type->elementType());
|
|
LLValue *rval = val->getRVal();
|
|
LLValue *vector = DtoAlloca(to);
|
|
|
|
for (int i = 0; i < dim; ++i) {
|
|
LLValue *elem = DtoGEPi(vector, 0, i);
|
|
DtoStore(rval, elem);
|
|
}
|
|
|
|
return new DVarValue(to, vector);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define STUB(x) DValue *x::toElem(IRState * p) {error("Exp type "#x" not implemented: %s", toChars()); fatal(); return 0; }
|
|
STUB(Expression);
|
|
STUB(ScopeExp);
|
|
|
|
#if DMDV2
|
|
STUB(SymbolExp);
|
|
STUB(PowExp);
|
|
STUB(PowAssignExp);
|
|
#endif
|
|
|
|
#define CONSTSTUB(x) LLConstant* x::toConstElem(IRState * p) { \
|
|
error("expression '%s' is not a constant", toChars()); \
|
|
if (!global.gag) \
|
|
fatal(); \
|
|
return NULL; \
|
|
}
|
|
CONSTSTUB(Expression);
|
|
CONSTSTUB(GEPExp);
|
|
CONSTSTUB(SliceExp);
|
|
CONSTSTUB(IndexExp);
|
|
CONSTSTUB(AssocArrayLiteralExp);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void obj_includelib(const char* lib)
|
|
{
|
|
size_t n = strlen(lib)+3;
|
|
char *arg = static_cast<char *>(mem.malloc(n));
|
|
strcpy(arg, "-l");
|
|
strncat(arg, lib, n);
|
|
global.params.linkswitches->push(arg);
|
|
}
|
|
|
|
void backend_init()
|
|
{
|
|
// LLVM_D_InitRuntime is done in Module::genLLVMModule
|
|
// since it requires the semantic pass to be done
|
|
}
|
|
|
|
void backend_term()
|
|
{
|
|
LLVM_D_FreeRuntime();
|
|
llvm::llvm_shutdown();
|
|
}
|