#include "cps/CodeGen.h" #include "cps/MemoryHandler.h" #include "cps/ExceptionHandler.h" #include "cps/TypeAST.h" #include #include using namespace llvm; using namespace cps; bool CodeGen::isObjectLValue(ExprAST *Expr) const { return dynamic_cast(Expr) || dynamic_cast(Expr) || dynamic_cast(Expr); } void CodeGen::transferObjects(const std::vector &Objects) { for (size_t Index : Objects) if (Index != size_t(-1)) Builder->CreateStore(Builder->getFalse(), StackObjects[Index].Alive); } Value *CodeGen::emitOwnedValue(ExprAST *Expr, const TypeInfo *Type, std::vector &Transfers) { if (!Type || !Type->IsClass) return emitCoercedExpr(Expr, Type); const TypeInfo *Source = getExprTypeInfo(Expr); if (!Source || !Source->sameType(Type)) return emitCoercedExpr(Expr, Type); Value *Value = nullptr; if (isObjectLValue(Expr)) { LValueInfo LV; if (!emitLValue(Expr, LV)) return nullptr; return copyClassValue(Type, LV.Addr, Transfers); } Value = emitCoercedExpr(Expr, Type); if (!Value) return nullptr; // A pending argument owns its result until all remaining arguments succeed. auto *Storage = materializeOperatorReceiver(Value, Type); if (typeNeedsDestruction(Type->Name)) Transfers.push_back(StackObjects.size() - 1); return Builder->CreateLoad(Type->LLVMType, Storage); } Value *CodeGen::copyClassValue(const TypeInfo *Type, Value *Address, std::vector &Transfers) { const FuncSig *Copy = nullptr; for (const FuncSig *Sig : FuncGen->getOverloads(Type->Name + ".NEW")) if (Sig->IsOverload && Sig->Params.size() == 1 && Sig->Params[0].IsByRef && !Sig->Params[0].IsArray && Type->sameType(resolveType(Sig->Params[0].TypeName))) Copy = Sig; if (!Copy) return Builder->CreateLoad(Type->LLVMType, Address); if (!checkOperatorAccess(*Copy)) return nullptr; auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), Type->LLVMType, "copy_object"); size_t Lifetime = registerStackObject(Storage, Type->Name, DiagnosticLine, false, true); if (Lifetime != size_t(-1)) StackObjects[Lifetime].Temporary = true; Builder->CreateStore(Types->getZeroValue(Type->Name), Storage); for (const auto &[Name, Field] : Type->Record.Fields) { if (typeNeedsConstruction(Field.TypeName) && !emitDefaultConstruct(Field.TypeName, Builder->CreateStructGEP(Type->LLVMType, Storage, Field.Index), DiagnosticLine)) return nullptr; } if (!FuncGen->emitCall(*Copy, {Storage, Address})) return nullptr; activateStackObject(Lifetime); Transfers.push_back(Lifetime); return Builder->CreateLoad(Type->LLVMType, Storage, "copied_value"); } void CodeGen::cleanupTemporaries(size_t Start) { if (!Builder->GetInsertBlock() || Builder->GetInsertBlock()->hasTerminator()) return; // Destructor emission can itself register temporaries. Copy each record before emitting. for (size_t I = StackObjects.size(); I > Start; --I) { auto Object = StackObjects[I - 1]; if (Object.Temporary) emitStackObjectCleanup(Object); } } bool CodeGen::typeNeedsConstruction(const std::string &TypeName) const { if (findMethod(TypeName, "NEW")) return true; const TypeInfo *Info = resolveType(TypeName); if (Info && Info->asRecord()) { for (const auto &[Name, Field] : Info->Record.Fields) if (typeNeedsConstruction(Field.TypeName)) return true; } return false; } bool CodeGen::emitConstruct(const std::string &TypeName, Value *Addr, const std::vector> &Args, int Line) { SaveAndRestore Location(DiagnosticLine, Line); const TypeInfo *Info = resolveType(TypeName); if (!Info) return false; if (Info->asRecord()) { for (const auto &[Name, Field] : Info->Record.Fields) { if (!typeNeedsConstruction(Field.TypeName)) continue; Value *FieldAddr = Builder->CreateStructGEP(Info->LLVMType, Addr, Field.Index); if (!emitDefaultConstruct(Field.TypeName, FieldAddr, Line)) return false; } } if (findMethod(TypeName, "NEW")) return emitMethodCall(TypeName, Addr, "NEW", Args, Line) != nullptr; if (!Args.empty()) { reportError("Type %s has no constructor NEW but arguments were supplied", TypeName.c_str()); return false; } return true; } bool CodeGen::emitDefaultConstruct(const std::string &TypeName, Value *Addr, int Line) { return emitConstruct(TypeName, Addr, {}, Line); } Value *CodeGen::symbolAddress(const SymbolInfo &Info) { if (!Info.IsReference) return Info.Storage; return Builder->CreateLoad(PointerType::getUnqual(*TheContext), Info.Storage, "object_address"); } void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const SymbolInfo *Symbol) { const TypeInfo *Info = resolveType(Target.TypeName); if (auto *New = dynamic_cast(Expr)) { SaveAndRestore Location(DiagnosticLine, New->getLine()); const TypeInfo *Class = resolveType(New->getClassName()); if (!Class || !Class->IsClass) { reportError("NEW requires a class type: %s", New->getClassName().c_str()); return; } if (!Info || !Info->sameType(Class)) { reportError("NEW target must have type %s", Class->Name.c_str()); return; } Value *Size = Builder->CreateIntCast(Types->getSizeOfConstant(Class->LLVMType), Type::getIntNTy(*TheContext, sizeof(std::size_t) * CHAR_BIT), false); // Method-only classes have zero-sized LLVM structs but still need // a non-null object address (MALLOC(0) deliberately returns null). Size = Builder->CreateSelect(Builder->CreateICmpEQ(Size, ConstantInt::get(Size->getType(), 0)), ConstantInt::get(Size->getType(), 1), Size); Value *Object = Memory->emitAllocate(Size, New->getLine()); Builder->CreateStore(Types->getZeroValue(Class->Name), Object); if (!emitConstruct(Class->Name, Object, New->getArgs(), New->getLine())) return; if (Symbol && Symbol->IsReference) { Builder->CreateStore(Object, Symbol->Storage); } else { Value *Val = Builder->CreateLoad(Class->LLVMType, Object); Builder->CreateStore(Val, Target.Addr); } return; } if (Info && Info->IsClass) { auto Resolution = resolveOperator(tok_assign, Info, getExprTypeInfo(Expr)); if (Resolution.Ambiguous) { reportError("Ambiguous assignment operator"); return; } if (const FuncSig *Sig = Resolution.Signature) { if (!checkOperatorAccess(*Sig)) return; std::vector Args = {Target.Addr}; if (marshalCallArgs(*Sig, std::vector{Expr}, DiagnosticLine, Args)) FuncGen->emitCall(*Sig, Args); return; } } Value *Val = emitCoercedExpr(Expr, Info); if (Val) Builder->CreateStore(Val, Target.Addr); } bool CodeGen::typeNeedsDestruction(const std::string &TypeName) const { if (findMethod(TypeName, "DELETE")) return true; const TypeInfo *Info = resolveType(TypeName); if (Info && Info->asRecord()) { for (const auto &[Name, Field] : Info->Record.Fields) if (typeNeedsDestruction(Field.TypeName)) return true; } return false; } bool CodeGen::emitDestroy(const std::string &TypeName, Value *Addr, int Line) { SaveAndRestore Location(DiagnosticLine, Line); const TypeInfo *Info = resolveType(TypeName); if (!Info) return false; if (findMethod(TypeName, "DELETE") && !emitMethodCall(TypeName, Addr, "DELETE", {}, Line)) return false; if (Info->asRecord()) { for (auto It = Info->Record.Fields.rbegin(); It != Info->Record.Fields.rend(); ++It) { const auto &Field = It->second; if (!typeNeedsDestruction(Field.TypeName)) continue; Value *FieldAddr = Builder->CreateStructGEP(Info->LLVMType, Addr, Field.Index); if (!emitDestroy(Field.TypeName, FieldAddr, Line)) return false; } } return true; } size_t CodeGen::registerStackObject(Value *Addr, const std::string &TypeName, int Line, bool IsArray, bool IsCopy) { if (!typeNeedsDestruction(TypeName)) return size_t(-1); auto *F = Builder->GetInsertBlock()->getParent(); IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin()); auto *Alive = Entry.CreateAlloca(Entry.getInt1Ty(), nullptr, "object_alive"); Entry.CreateStore(Entry.getFalse(), Alive); AllocaInst *Count = nullptr; if (IsArray) { Count = Entry.CreateAlloca(Entry.getInt64Ty(), nullptr, "object_count"); Entry.CreateStore(Entry.getInt64(0), Count); } StackObjects.push_back({Addr, TypeName, Alive, Count, Line, IsCopy}); // This DECLARE can execute repeatedly, including after a backward loop edge. emitStackObjectCleanup(StackObjects.back()); return StackObjects.size() - 1; } void CodeGen::activateStackObject(size_t Index, Value *Count) { if (Index == size_t(-1)) return; const auto &Object = StackObjects[Index]; if (Object.Count) Builder->CreateStore(Count, Object.Count); Builder->CreateStore(Builder->getTrue(), Object.Alive); } void CodeGen::emitStackObjectCleanup(const StackObject &Object) { auto *F = Builder->GetInsertBlock()->getParent(); auto *Destroy = BasicBlock::Create(*TheContext, "destroy_object", F); auto *Done = BasicBlock::Create(*TheContext, "destroyed_object", F); Builder->CreateCondBr(Builder->CreateLoad(Builder->getInt1Ty(), Object.Alive), Destroy, Done); Builder->SetInsertPoint(Destroy); // Clear before the user method: a throwing destructor must never run twice. Builder->CreateStore(Builder->getFalse(), Object.Alive); if (!Object.Count) { emitDestroy(Object.TypeName, Object.Addr, Object.Line); } else { Value *Base = Builder->CreateLoad(PointerType::getUnqual(*TheContext), Object.Addr); Value *Count = Builder->CreateLoad(Builder->getInt64Ty(), Object.Count); auto *Preheader = Builder->GetInsertBlock(); auto *Loop = BasicBlock::Create(*TheContext, "destroy_elements", F); auto *Body = BasicBlock::Create(*TheContext, "destroy_element", F); Builder->CreateBr(Loop); Builder->SetInsertPoint(Loop); auto *Index = Builder->CreatePHI(Builder->getInt64Ty(), 2); Index->addIncoming(Count, Preheader); Builder->CreateCondBr(Builder->CreateICmpNE(Index, Builder->getInt64(0)), Body, Done); Builder->SetInsertPoint(Body); Value *Previous = Builder->CreateSub(Index, Builder->getInt64(1)); Value *Element = Builder->CreateGEP(resolveType(Object.TypeName)->LLVMType, Base, Previous); emitDestroy(Object.TypeName, Element, Object.Line); Index->addIncoming(Previous, Builder->GetInsertBlock()); Builder->CreateBr(Loop); Builder->SetInsertPoint(Done); return; } Builder->CreateBr(Done); Builder->SetInsertPoint(Done); } void CodeGen::finishStackObjects(Function *F) { Exceptions->fillCleanups(F, [this](bool TemporariesOnly) { for (auto It = StackObjects.rbegin(); It != StackObjects.rend(); ++It) if (!TemporariesOnly || It->Temporary) emitStackObjectCleanup(*It); }); }