From a44f1246dbe24fc813a212a5ef3f5811b1d6402a Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 26 Jul 2026 17:42:08 +0000 Subject: feat: whole arrays (and records) as FUNCTION/PROCEDURE parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parameter syntax lands end-to-end: ARRAY OF T (rank 1, bounds travel with the argument) and ARRAY[l:u,...] OF T (any rank, literal bounds as a checked contract). Element type and rank must match exactly. - Flattened ABI: each rank-R array parameter becomes a data pointer plus kBoundArgsPerDim i64 bounds per dimension. The callee rebuilds ArrayMetadata from those bounds (ArrayHandler::bindArrayParameter via a binder callback injected by CodeGen, so FunctionGen stays ignorant of the array subsystem); the checked-indexing pipeline is reused unchanged. - BYVAL (the CIE default) copies the whole buffer at callee entry (malloc+memcpy, never freed — consistent with the string policy) so callee writes stay invisible; BYREF binds the caller's buffer. - Bounded-parameter contract: compile-time comparison when the caller's bounds are constants, else a runtime [Fatal] check (RuntimeCheck::emitArrayArgBoundsCheck). - marshalCallArgs gains the array branch and a call-site line number (CallStmtAST now carries one); whole-array-to-scalar keeps a clear error, FUNCTION RETURNS ARRAY is a dedicated parse error. - Verified: BYREF fill/sort-style mutation, BYVAL copy invisibility, 2-D bounded params, ARRAY OF with field writes through the parameter, bounds-contract mismatches at compile time and run time. Co-Authored-By: Claude Fable 5 --- lib/CodeGen/ArrayHandler.cc | 152 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 12 deletions(-) (limited to 'lib/CodeGen/ArrayHandler.cc') diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc index d21d91c..c647d58 100644 --- a/lib/CodeGen/ArrayHandler.cc +++ b/lib/CodeGen/ArrayHandler.cc @@ -91,8 +91,6 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { std::vector Lows; std::vector Highs; - std::vector Dims; - Value *TotalElements = ConstantInt::get(*TheContext, APInt(64, 1)); for (const auto &Pair : Stmt->getBounds()) { Value *L = CG.emitExpr(Pair.first.get()); @@ -104,18 +102,11 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { Lows.push_back(L); Highs.push_back(R); - - Value *Diff = Builder->CreateSub(R, L, Name + "_dim_diff"); - Value *DimSize = Builder->CreateAdd(Diff, ConstantInt::get(*TheContext, APInt(64, 1)), Name + "_dim_size"); - Dims.push_back(DimSize); - TotalElements = Builder->CreateMul(TotalElements, DimSize, Name + "_total_elems"); } - std::vector Multipliers(Rank); - Multipliers[Rank - 1] = ConstantInt::get(*TheContext, APInt(64, 1)); - for (int i = Rank - 2; i >= 0; --i) { - Multipliers[i] = Builder->CreateMul(Multipliers[i + 1], Dims[i + 1], Name + "_mult"); - } + std::vector Multipliers; + Value *TotalElements = nullptr; + computeDimsAndMultipliers(Name, Lows, Highs, Multipliers, TotalElements); ArrayMetadata Meta; Meta.Rank = Rank; @@ -144,6 +135,30 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { CG.registerSymbol(Name, Alloca, ElemInfo->Name, true); } +void ArrayHandler::computeDimsAndMultipliers(const std::string &Name, + const std::vector &Lows, + const std::vector &Highs, + std::vector &Multipliers, + Value *&TotalElements) { + int Rank = static_cast(Lows.size()); + std::vector Dims; + TotalElements = ConstantInt::get(*TheContext, APInt(64, 1)); + + for (int I = 0; I < Rank; ++I) { + Value *Diff = Builder->CreateSub(Highs[I], Lows[I], Name + "_dim_diff"); + Value *DimSize = Builder->CreateAdd(Diff, ConstantInt::get(*TheContext, APInt(64, 1)), + Name + "_dim_size"); + Dims.push_back(DimSize); + TotalElements = Builder->CreateMul(TotalElements, DimSize, Name + "_total_elems"); + } + + Multipliers.assign(Rank, nullptr); + Multipliers[Rank - 1] = ConstantInt::get(*TheContext, APInt(64, 1)); + for (int I = Rank - 2; I >= 0; --I) { + Multipliers[I] = Builder->CreateMul(Multipliers[I + 1], Dims[I + 1], Name + "_mult"); + } +} + bool ArrayHandler::emitCheckedIndices(const std::string &Name, const std::vector> &IndexExprs, int Line, @@ -238,6 +253,119 @@ std::map ArrayHandler::exchangeTable(std::map &LBs, + const std::vector &UBs, + bool MakeCopy, + CodeGen &CG) { + const TypeInfo *ElemInfo = Types.resolve(ElemTypeName); + if (!ElemInfo || !ElemInfo->LLVMType || ElemInfo->isVoid()) { + CG.reportError("Unknown array element type %s", ElemTypeName.c_str()); + return; + } + + std::vector Multipliers; + Value *TotalElements = nullptr; + computeDimsAndMultipliers(Name, LBs, UBs, Multipliers, TotalElements); + + ArrayMetadata Meta; + Meta.Rank = static_cast(LBs.size()); + Meta.ElementTypeName = ElemInfo->Name; + Meta.ElementType = ElemInfo->LLVMType; + Meta.ElementSizeC = Types.getSizeOfConstant(ElemInfo->LLVMType); + Meta.LowerBounds = LBs; + Meta.UpperBounds = UBs; + Meta.Multipliers = std::move(Multipliers); + ArrayTable[Name] = Meta; + + Value *Ptr = DataPtr; + if (MakeCopy) { + // BYVAL: whole-buffer copy so callee writes stay invisible to the + // caller. The copy is malloc'd and never freed, consistent with the + // project-wide string policy. + Value *TotalBytes = Builder->CreateMul(TotalElements, Meta.ElementSizeC, + Name + "_copy_bytes"); + Value *Copy = Builder->CreateCall(MallocFunc, TotalBytes, Name + "_copy"); + FunctionType *MemcpyType = FunctionType::get( + PointerType::getUnqual(*TheContext), + {PointerType::getUnqual(*TheContext), PointerType::getUnqual(*TheContext), + Type::getInt64Ty(*TheContext)}, + false); + FunctionCallee MemcpyFunc = TheModule->getOrInsertFunction("memcpy", MemcpyType); + Builder->CreateCall(MemcpyFunc, {Copy, DataPtr, TotalBytes}); + Ptr = Copy; + } + + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + AllocaInst *Alloca = CG.CreateEntryBlockAlloca(TheFunction, PointerType::getUnqual(*TheContext), Name); + Builder->CreateStore(Ptr, Alloca); + + CG.registerSymbol(Name, Alloca, ElemInfo->Name, true); +} + +bool ArrayHandler::emitArrayArgument(ExprAST *ArgExpr, + const std::string &ElemTypeName, + int Rank, + const std::vector> &DeclaredBounds, + const std::string &Callee, + int Line, + CodeGen &CG, + std::vector &Out) { + auto *Var = dynamic_cast(ArgExpr); + const ArrayMetadata *Meta = Var ? getMetadata(Var->getName()) : nullptr; + if (!Meta) { + CG.reportError("Argument to %s must be a whole array variable (ARRAY OF %s)", + Callee.c_str(), ElemTypeName.c_str()); + return false; + } + if (Meta->ElementTypeName != ElemTypeName) { + CG.reportError("Array argument %s to %s must have element type %s (got %s)", + Var->getName().c_str(), Callee.c_str(), + ElemTypeName.c_str(), Meta->ElementTypeName.c_str()); + return false; + } + if (Meta->Rank != Rank) { + CG.reportError("Array argument %s to %s must have %d dimension%s (got %d)", + Var->getName().c_str(), Callee.c_str(), + Rank, Rank == 1 ? "" : "s", Meta->Rank); + return false; + } + + // Bounded parameters are a contract: verified at compile time when the + // caller's bounds are constants (the common case), at run time otherwise. + for (size_t D = 0; D < DeclaredBounds.size(); ++D) { + int64_t DeclLo = DeclaredBounds[D].first; + int64_t DeclHi = DeclaredBounds[D].second; + auto *ActLo = dyn_cast(Meta->LowerBounds[D]); + auto *ActHi = dyn_cast(Meta->UpperBounds[D]); + if (ActLo && ActHi) { + if (ActLo->getSExtValue() != DeclLo || ActHi->getSExtValue() != DeclHi) { + CG.reportError("Array argument %s to %s has bounds [%lld:%lld] but the parameter declares [%lld:%lld]", + Var->getName().c_str(), Callee.c_str(), + static_cast(ActLo->getSExtValue()), + static_cast(ActHi->getSExtValue()), + static_cast(DeclLo), + static_cast(DeclHi)); + return false; + } + } else { + RuntimeChecker.emitArrayArgBoundsCheck(Meta->LowerBounds[D], Meta->UpperBounds[D], + DeclLo, DeclHi, Line); + } + } + + Value *DataPtr = getArrayBasePointer(Var->getName()); + if (!DataPtr) return false; + Out.push_back(DataPtr); + for (int D = 0; D < Rank; ++D) { + Out.push_back(Meta->LowerBounds[D]); + Out.push_back(Meta->UpperBounds[D]); + } + return true; +} + bool ArrayHandler::tryEmitArrayOutput(ExprAST *Expr, CodeGen &CG) { std::string Name; const std::vector> *IndexExprs = nullptr; -- cgit v1.3.1-4-g156e