#pragma once #include "cps/Identifier.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "cps/AST.h" #include "cps/RuntimeCheck.h" #include "cps/TypeSystem.h" #include #include #include #include namespace cps { struct ArrayMetadata { int Rank = 0; std::string ElementTypeName; llvm::Type *ElementType = nullptr; llvm::Constant *ElementSizeC = nullptr; // i64 sizeof constant (target-folded) std::vector LowerBounds; std::vector UpperBounds; std::vector Multipliers; }; class CodeGen; class ArrayHandler { llvm::LLVMContext *TheContext; llvm::IRBuilder<> *Builder; llvm::Module *TheModule; IdentifierMap *Symbols; TypeSystem &Types; IdentifierMap ArrayTable; RuntimeCheck &RuntimeChecker; llvm::FunctionCallee MallocFunc; llvm::Value *computeFlatIndex(const std::string &Name, const std::vector &Indices); llvm::Value *getArrayBasePointer(const std::string &Name); llvm::Value *getElementPointer(const std::string &Name, llvm::Value *Offset); const ArrayMetadata *getMetadata(const std::string &Name) const; // Shared bounds math: DimSize_i = (Upper_i - Lower_i) + 1, row-major // multipliers back-to-front, TotalElements = product of the sizes. void computeDimsAndMultipliers(const std::string &Name, const std::vector &Lows, const std::vector &Highs, std::vector &Multipliers, llvm::Value *&TotalElements); bool emitCheckedIndices(const std::string &Name, const std::vector> &IndexExprs, int Line, bool RequireExact, CodeGen &CG, std::vector &Out); llvm::Value *loadElement(const std::string &Name, llvm::Value *Offset, CodeGen &CG); void emitPrintLoop(const std::string &Name, int CurrentDim, std::vector CurrentIndices, CodeGen &CG); public: ArrayHandler(llvm::LLVMContext &C, llvm::IRBuilder<> &B, llvm::Module &M, IdentifierMap &Sym, TypeSystem &TS, RuntimeCheck &RC); void setupExternalFunctions(); void emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG); void emitArrayAssign(ArrayAssignStmtAST *Stmt, CodeGen &CG); llvm::Value *emitArrayAccess(ArrayAccessExprAST *Expr, CodeGen &CG); bool tryEmitArrayOutput(ExprAST *Expr, CodeGen &CG); // Full-rank checked element address (bounds checks included); reports the // element type name through ElemTypeNameOut. The lvalue layer builds // field/deref accesses on top of this. llvm::Value *emitElementAddress(const std::string &Name, const std::vector> &IndexExprs, int Line, CodeGen &CG, std::string &ElemTypeNameOut); llvm::Value *emitElementAddress(ArrayAccessExprAST *Expr, CodeGen &CG, std::string &ElemTypeNameOut); // Swap in a fresh table (function bodies get their own scope, mirroring // the Symbols save/clear/restore discipline); returns the previous table. IdentifierMap exchangeTable(IdentifierMap NewTable); // Copy for block-scoping: control-flow bodies snapshot before and restore // after, so an array declared inside a branch cannot be referenced past it // (its buffer setup and bound SSA values live inside the branch). IdentifierMap snapshotTable() const { return ArrayTable; } // Callee side of an array parameter: rebuild metadata from the incoming // data pointer + per-dim bounds. MakeCopy (BYVAL) mallocs and memcpys the // whole buffer so callee writes stay invisible to the caller. void bindArrayParameter(const std::string &Name, const std::string &ElemTypeName, llvm::Value *DataPtr, const std::vector &LBs, const std::vector &UBs, bool MakeCopy, CodeGen &CG); // Call side: validate a whole-array argument against the declared // parameter (element type and rank exactly; bounded params check their // bounds at compile time when constant, else at run time) and append the // data pointer plus per-dim bounds to Out. bool emitArrayArgument(ExprAST *ArgExpr, const std::string &ElemTypeName, int Rank, const std::vector> &DeclaredBounds, const std::string &Callee, int Line, CodeGen &CG, std::vector &Out); }; } // namespace cps