diff --git a/dmd2/expression.c b/dmd2/expression.c index 2790afa1..00cef6e4 100644 --- a/dmd2/expression.c +++ b/dmd2/expression.c @@ -4369,6 +4369,8 @@ StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions * this->ctorinit = 0; #if IN_LLVM constType = NULL; + this->inProgressMemory = NULL; + this->globalVar = NULL; #endif this->origin = this; this->stageflags = 0; diff --git a/dmd2/expression.h b/dmd2/expression.h index 67ad55f2..9272ddee 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -76,6 +76,7 @@ class DValue; namespace llvm { class Constant; class ConstantInt; + class GlobalVariable; class StructType; } #endif @@ -628,8 +629,16 @@ struct StructLiteralExp : Expression Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_LLVM DValue* toElem(IRState* irs); + // With the introduction of pointers returned from CTFE, struct literals can + // now contain pointers to themselves. While in toElem, contains a pointer + // to the memory used to build the literal for resolving such references. + llvm::Value* inProgressMemory; + llvm::Constant *toConstElem(IRState *irs); llvm::StructType *constType; + // A global variable for taking the address of this struct literal constant, + // if it already exists. Used to resolve self-references. + llvm::GlobalVariable *globalVar; /// Set if this is really the result of a struct .init access and should be /// resolved codegen'd as an access to the given SymbolDeclaration. diff --git a/gen/toir.cpp b/gen/toir.cpp index 57e3615e..fe8ae5d2 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -36,11 +36,13 @@ #include "gen/typeinf.h" #include "gen/utils.h" #include "gen/warnings.h" +#include "ir/irtypeclass.h" #include "ir/irtypestruct.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include #include +#include #include // Needs other includes. @@ -1102,6 +1104,25 @@ LLConstant* CastExp::toConstElem(IRState* p) } return DtoBitCast(value, DtoType(tb)); } + else if (tb->ty == Tclass && e1->type->ty == Tclass) { + assert(e1->op == TOKclassreference); + ClassDeclaration* cd = static_cast(e1)->originalClass(); + + llvm::Constant* instance = e1->toConstElem(p); + if (InterfaceDeclaration* it = static_cast(tb)->sym->isInterfaceDeclaration()) { + assert(it->isBaseOf(cd, NULL)); + + IrTypeClass* typeclass = cd->type->irtype->isClass(); + + // find interface impl + size_t i_index = typeclass->getInterfaceIndex(it); + assert(i_index != ~0UL); + + // offset pointer + instance = DtoGEPi(instance, 0, i_index); + } + return DtoBitCast(instance, DtoType(tb)); + } else { goto Lerr; } @@ -1287,9 +1308,36 @@ LLConstant* AddrExp::toConstElem(IRState* p) assert(type->toBasetype()->ty == Tpointer); return DtoBitCast(gep, DtoType(type)); } - else if ( - e1->op == TOKstructliteral || - e1->op == TOKslice) + else if (e1->op == TOKstructliteral) + { + StructLiteralExp* se = static_cast(e1); + + if (se->globalVar) + { + Logger::cout() << "Returning existing global: " << *se->globalVar << '\n'; + return se->globalVar; + } + + se->globalVar = new llvm::GlobalVariable(*p->module, + DtoType(e1->type), false, llvm::GlobalValue::InternalLinkage, 0); + + llvm::Constant* constValue = se->toConstElem(p); + if (constValue->getType() != se->globalVar->getType()->getContainedType(0)) + { + llvm::GlobalVariable* finalGlobalVar = new llvm::GlobalVariable( + *p->module, constValue->getType(), false, + llvm::GlobalValue::InternalLinkage, 0); + se->globalVar->replaceAllUsesWith( + DtoBitCast(finalGlobalVar, se->globalVar->getType())); + se->globalVar->eraseFromParent(); + se->globalVar = finalGlobalVar; + } + se->globalVar->setInitializer(constValue); + se->globalVar->setAlignment(e1->type->alignsize()); + + return se->globalVar; + } + else if (e1->op == TOKslice) { error("non-constant expression '%s'", toChars()); fatal(); @@ -2873,11 +2921,13 @@ DValue* StructLiteralExp::toElem(IRState* p) return new DVarValue(type, initsym); } + if (inProgressMemory) return new DVarValue(type, inProgressMemory); + // make sure the struct is fully resolved sd->codegen(Type::sir); // alloca a stack slot - LLValue* mem = DtoRawAlloca(DtoType(type), 0, ".structliteral"); + inProgressMemory = DtoRawAlloca(DtoType(type), 0, ".structliteral"); // ready elements data assert(elements && "struct literal has null elements"); @@ -2885,7 +2935,7 @@ DValue* StructLiteralExp::toElem(IRState* p) Expression** exprs = (Expression**)elements->data; // might be reset to an actual i8* value so only a single bitcast is emitted. - LLValue* voidptr = mem; + LLValue* voidptr = inProgressMemory; unsigned offset = 0; // go through fields @@ -2933,7 +2983,7 @@ DValue* StructLiteralExp::toElem(IRState* p) } // get a pointer to this field - DVarValue field(vd->type, vd, DtoIndexStruct(mem, sd, vd)); + DVarValue field(vd->type, vd, DtoIndexStruct(inProgressMemory, sd, vd)); // store the initializer there DtoAssign(loc, &field, val, TOKconstruct, true); @@ -2957,7 +3007,9 @@ DValue* StructLiteralExp::toElem(IRState* p) voidptr = write_zeroes(voidptr, offset, sd->structsize); // return as a var - return new DVarValue(type, mem); + DValue* result = new DVarValue(type, inProgressMemory); + inProgressMemory = 0; + return result; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2985,8 +3037,7 @@ LLConstant* StructLiteralExp::toConstElem(IRState* p) sd->codegen(Type::sir); std::map varInits; - - size_t nexprs = elements->dim; + const size_t nexprs = elements->dim; for (size_t i = 0; i < nexprs; i++) { if ((*elements)[i]) @@ -3004,8 +3055,86 @@ llvm::Constant* ClassReferenceExp::toConstElem(IRState *p) toChars(), type->toChars()); LOG_SCOPE; - // FIXME: Handle type->sym->isInterfaceDeclaration(). - return value->toConstElem(p); + ClassDeclaration* origClass = originalClass(); + + origClass->codegen(Type::sir); + + if (value->globalVar) + { + IF_LOG Logger::cout() << "Using existing global: " << *value->globalVar << '\n'; + } + else + { + value->globalVar = new llvm::GlobalVariable(*p->module, + origClass->type->irtype->isClass()->getMemoryLLType(), + false, llvm::GlobalValue::InternalLinkage, 0); + + std::map varInits; + + // Unfortunately, ClassReferenceExp::getFieldAt is badly broken – it + // places the base class fields _after_ those of the subclass. + { + const size_t nexprs = value->elements->dim; + + std::stack classHierachy; + ClassDeclaration* cur = origClass; + while (cur) + { + classHierachy.push(cur); + cur = cur->baseClass; + } + size_t i = 0; + while (!classHierachy.empty()) + { + cur = classHierachy.top(); + classHierachy.pop(); + for (size_t j = 0; j < cur->fields.dim; ++j) + { + if ((*value->elements)[i]) + { + VarDeclaration* field = cur->fields[j]; + IF_LOG Logger::println("Getting initializer for: %s", field->toChars()); + LOG_SCOPE; + varInits[field] = (*value->elements)[i]->toConstElem(p); + } + ++i; + } + } + assert(i == nexprs); + } + + llvm::Constant* constValue = origClass->ir.irStruct->createInitializerConstant(varInits); + + if (constValue->getType() != value->globalVar->getType()->getContainedType(0)) + { + llvm::GlobalVariable* finalGlobalVar = new llvm::GlobalVariable(*p->module, + constValue->getType(), false, llvm::GlobalValue::InternalLinkage, 0); + value->globalVar->replaceAllUsesWith( + DtoBitCast(finalGlobalVar, value->globalVar->getType())); + value->globalVar->eraseFromParent(); + value->globalVar = finalGlobalVar; + } + value->globalVar->setInitializer(constValue); + } + + llvm::Constant* result = value->globalVar; + + assert(type->ty == Tclass); + ClassDeclaration* targetClass = static_cast(type)->sym; + if (InterfaceDeclaration* it = targetClass->isInterfaceDeclaration()) { + assert(it->isBaseOf(origClass, NULL)); + + IrTypeClass* typeclass = origClass->type->irtype->isClass(); + + // find interface impl + size_t i_index = typeclass->getInterfaceIndex(it); + assert(i_index != ~0UL); + + // offset pointer + result = DtoGEPi(result, 0, i_index); + } + + return DtoBitCast(result, DtoType(type)); } //////////////////////////////////////////////////////////////////////////////////////////