diff --git a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h index 797fd5e..e1ae0a9 100644 --- a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h +++ b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h @@ -128,6 +128,19 @@ private: std::map m_tempLocations; int m_nextTempReg = 0; // Prochain registre t0-t9 disponible int m_tempVarCounter = 0; // Compteur pour variables temporaires en RAM + + // Function call management + struct FunctionCallContext { + int paramCount; + std::string functionLabel; + std::map paramRegMap; + }; + + std::shared_ptr m_currentFunctionCall; + + // Flag pour distinguer PARAM pour syscall vs function call + bool m_preparingFunctionCall = false; + int m_functionCallParamIndex = 0; // =================================================================== // GÉNÉRATION DE LA SECTION DATA @@ -260,12 +273,34 @@ private: break; case TACInstruction::OpCode::RETURN: - m_assembly << " ret\n"; + // Utiliser FUNC_RETURN pour les fonctions avec FUNC_BEGIN + if (m_currentFunctionCall) { + GenerateFuncReturn(instr); + } else { + // RETURN normal (existant) - peut-être déjà géré ailleurs + m_assembly << " ret\n"; + } break; case TACInstruction::OpCode::NOP: m_assembly << " nop\n"; break; + + case TACInstruction::OpCode::FUNC_BEGIN: + GenerateFuncBegin(instr); + break; + + case TACInstruction::OpCode::FUNC_END: + GenerateFuncEnd(instr); + break; + + case TACInstruction::OpCode::FUNC_PARAM_IN: + GenerateFuncParamIn(instr); + break; + + case TACInstruction::OpCode::RETURN_VAL: + GenerateReturnVal(instr); + break; default: AddComment("WARNING: Unsupported TAC instruction"); @@ -379,6 +414,173 @@ private: // =================================================================== // GÉNÉRATION DES INSTRUCTIONS // =================================================================== + + // Génération pour FUNC_BEGIN + void GenerateFuncBegin(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Function Begin: " + instr->GetDest()->ToString()); + } + + std::string functionLabel = instr->GetDest()->GetValue(); + + // Générer le label de la fonction + m_assembly << "\n" << functionLabel << ":\n"; + + // Initialiser le contexte d'appel + m_currentFunctionCall = std::make_shared(); + m_currentFunctionCall->functionLabel = functionLabel; + m_currentFunctionCall->paramCount = 0; + + AddComment("Parameters are passed in r0-r9"); + } + + // Génération pour FUNC_PARAM_IN + void GenerateFuncParamIn(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Read parameter: " + instr->GetOp1()->ToString()); + } + + std::string paramName = instr->GetOp1()->GetValue(); + + // Le paramètre est dans r0, r1, r2, etc. selon l'ordre + int paramReg = m_currentFunctionCall->paramCount; + if (paramReg >= 10) { + throw std::runtime_error("Too many parameters (max 10 with r0-r9)"); + } + + m_currentFunctionCall->paramRegMap[paramName] = paramReg; + m_currentFunctionCall->paramCount++; + + // Le paramètre est déjà dans rX, le copier dans le temporaire destination + std::string sourceReg = "r" + std::to_string(paramReg); + StoreResult(instr->GetDest(), sourceReg); + + if (m_context.debugOutput) { + AddComment("Parameter " + paramName + " from " + sourceReg); + } + } + + // Génération pour FUNC_RETURN + void GenerateFuncReturn(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Function return"); + } + + // Nettoyer le contexte + m_currentFunctionCall.reset(); + + // Retourner (le résultat est déjà dans r0 via RETURN_VAL) + m_assembly << " ret\n"; + } + + // Génération pour FUNC_END + void GenerateFuncEnd(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Function End"); + } + // Rien à faire, juste marquer la fin + } + + // Génération pour RETURN_VAL + void GenerateReturnVal(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Set return value: " + instr->GetDest()->ToString()); + } + + std::string returnName = instr->GetDest()->GetValue(); + + // Charger la valeur à retourner dans r0 (convention de retour) + std::string valueReg = LoadOperand(instr->GetOp1(), "r0"); + + // Si la valeur est déjà dans r0, rien à faire + if (valueReg != "r0") { + m_assembly << " mov r0, " << valueReg << "\n"; + } + + if (m_context.debugOutput) { + AddComment("Return value " + returnName + " in r0"); + } + } + + // Génération pour CALL (peut déjà exister - vérifier avant d'ajouter) + void GenerateCall(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Call function: " + instr->GetOp1()->ToString()); + } + + std::string functionLabel = instr->GetOp1()->GetValue(); + + // Réinitialiser le compteur de paramètres après l'appel + m_functionCallParamIndex = 0; + m_preparingFunctionCall = false; + + // Effectuer l'appel + m_assembly << " call " << functionLabel << "\n"; + + // Le résultat est dans r0 (convention) + // Stocker dans le temporaire de destination + if (instr->GetDest()) { + StoreResult(instr->GetDest(), "r0"); + } + } + + // Génération pour PARAM - DISTINGUER syscall vs function call + void GenerateParam(std::shared_ptr instr) { + if (m_context.debugOutput) { + AddComment("Parameter: " + instr->GetDest()->ToString()); + } + + // Déterminer si c'est pour un syscall ou un appel de fonction + // en regardant si on est dans un contexte d'appel de fonction + if (m_preparingFunctionCall) { + // C'est pour un appel de fonction → charger dans rN + if (m_functionCallParamIndex >= 10) { + throw std::runtime_error("Too many parameters for function call (max 10)"); + } + + std::string targetReg = "r" + std::to_string(m_functionCallParamIndex); + std::string paramReg = LoadOperand(instr->GetDest(), targetReg); + + if (paramReg != targetReg) { + m_assembly << " mov " << targetReg << ", " << paramReg << "\n"; + } + + m_functionCallParamIndex++; + } else { + // C'est pour un syscall (Print, etc.) → empiler sur la stack + std::string paramReg = LoadOperand(instr->GetDest(), "r0"); + m_assembly << " push " << paramReg << "\n"; + } + } + + // Méthode helper pour détecter si les PARAM suivants sont pour un CALL + void DetectFunctionCallContext(size_t currentInstrIndex) { + // Regarder les instructions suivantes pour détecter un CALL de fonction + const auto& instructions = m_tacProgram.GetInstructions(); + + // Compter les PARAM consécutifs après l'instruction courante + size_t paramCount = 0; + size_t idx = currentInstrIndex + 1; + + while (idx < instructions.size() && + instructions[idx]->GetOpCode() == TACInstruction::OpCode::PARAM) { + paramCount++; + idx++; + } + + // Vérifier ce qui vient après les PARAM + if (idx < instructions.size()) { + auto nextInstr = instructions[idx]; + + // Si c'est un CALL de fonction utilisateur, activer le flag + if (nextInstr->GetOpCode() == TACInstruction::OpCode::CALL) { + m_preparingFunctionCall = true; + m_functionCallParamIndex = 0; + } + // Sinon (PRINT, etc.), c'est pour un syscall + } + } + void GenerateBinaryOp(std::shared_ptr instr) { // Charger les opérandes @@ -474,11 +676,6 @@ private: std::vector> m_printParams; - void GenerateParam(std::shared_ptr instr) { - // Accumuler les paramètres pour le prochain PRINT/CALL - m_printParams.push_back(instr->GetDest()); - } - void GeneratePrint(std::shared_ptr instr) { // Sauvegarder r0, r1 m_assembly << " push r0\n"; @@ -515,20 +712,6 @@ private: m_printParams.clear(); } - void GenerateCall(std::shared_ptr instr) { - // Générer l'appel de fonction - std::string functionName = instr->GetOp1() ? - instr->GetOp1()->ToString() : - instr->GetDest()->ToString(); - - m_assembly << " call " << functionName << "\n"; - - // Si on a une destination, sauvegarder le résultat - if (instr->GetDest() && instr->GetOp1()) { - // Le résultat est dans r0 par convention - StoreResult(instr->GetDest(), "r0"); - } - } // =================================================================== // HELPER : Obtenir la taille d'une variable diff --git a/core/story-manager/src/compiler/tac.h b/core/story-manager/src/compiler/tac.h index 6873021..16a16fd 100644 --- a/core/story-manager/src/compiler/tac.h +++ b/core/story-manager/src/compiler/tac.h @@ -113,7 +113,13 @@ public: // Special PRINT, // Print (syscall 4) SYSCALL, // Generic syscall - NOP // No operation + NOP, // No operation + + // Function definition + FUNC_BEGIN, // Begin function definition + FUNC_PARAM_IN, // Read input parameter from stack + RETURN_VAL, // Set return value + FUNC_END, // End function definition }; TACInstruction(OpCode op) @@ -252,6 +258,18 @@ public: case OpCode::SYSCALL: ss << "syscall " << m_dest->ToString(); break; + case OpCode::FUNC_BEGIN: + ss << "func_begin " << m_dest->ToString(); + break; + case OpCode::FUNC_END: + ss << "func_end"; + break; + case OpCode::FUNC_PARAM_IN: + ss << m_dest->ToString() << " = param_in " << m_op1->ToString(); + break; + case OpCode::RETURN_VAL: + ss << "return_val " << m_dest->ToString() << " = " << m_op1->ToString(); + break; case OpCode::NOP: ss << "nop"; break; @@ -328,6 +346,7 @@ private: #include "while_loop_node.h" #include "break_node.h" #include "continue_node.h" +#include "function_exit_node.h" // =================================================================== // SECTION 2: CLASSE TACGenerator - MODIFICATIONS @@ -400,6 +419,17 @@ private: // Variables disponibles pour résolution std::vector> m_variables; + // Context de fonction + struct FunctionContext { + std::string functionName; + std::string functionUuid; + std::vector parameters; // Noms des paramètres + std::vector returnValues; // Noms des valeurs de retour + std::string returnLabel; // Label de sortie + }; + + std::shared_ptr m_currentFunction; + // =================================================================== // HELPERS // =================================================================== @@ -538,6 +568,12 @@ private: GenerateNode(node->children[i]); } } + else if (node->IsType()) { + return GenerateFunctionEntryNode(node); + } + else if (node->IsType()) { + return GenerateFunctionExitNode(node); + } // =================================================================== // NŒUDS SYSCALL @@ -560,7 +596,7 @@ private: } // =================================================================== - // OUR LES BOUCLES + // POUR LES BOUCLES // =================================================================== else if (node->IsType()) { std::cout << " -> Type: ForLoopNode\n"; @@ -594,9 +630,195 @@ private: } // =================================================================== - // GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement) + // GÉNÉRATION PAR TYPE DE NŒUD // =================================================================== + std::shared_ptr GenerateFunctionEntryNode(std::shared_ptr node) { + auto* entryNode = node->GetAs(); + if (!entryNode) return nullptr; + + std::cout << " Generating TAC for FunctionEntryNode\n"; + + // Créer un contexte de fonction + m_currentFunction = std::make_shared(); + m_currentFunction->functionName = "function_" + node->GetId(); + + // Générer FUNC_BEGIN + auto funcLabel = std::make_shared( + TACOperand::Type::LABEL, + m_currentFunction->functionName + ); + + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::FUNC_BEGIN, + funcLabel, + nullptr, + nullptr + )); + + // Pour chaque paramètre, générer FUNC_PARAM_IN + for (const auto& param : entryNode->GetParameters()) { + // Créer un temporaire pour stocker le paramètre + auto paramTemp = NewTemp(); + m_currentFunction->parameters.push_back(paramTemp->GetValue()); + + auto paramName = std::make_shared( + TACOperand::Type::VARIABLE, + param.name + ); + + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::FUNC_PARAM_IN, + paramTemp, + paramName, + nullptr + )); + + std::cout << " Parameter: " << param.name + << " -> " << paramTemp->GetValue() << "\n"; + } + + return funcLabel; + } + + // Génération pour FunctionExitNode + std::shared_ptr GenerateFunctionExitNode(std::shared_ptr node) { + auto* exitNode = node->GetAs(); + if (!exitNode) return nullptr; + + std::cout << " Generating TAC for FunctionExitNode: " + << exitNode->GetExitLabel() << "\n"; + + // Pour chaque valeur de retour, évaluer et générer RETURN_VAL + size_t portIndex = 1; // Port 0 est l'exécution + for (const auto& returnValue : exitNode->GetReturnValues()) { + // Récupérer l'input qui fournit cette valeur + auto it = node->dataInputs.find(portIndex); + if (it != node->dataInputs.end()) { + auto valueOperand = GenerateNode(it->second); + + auto returnName = std::make_shared( + TACOperand::Type::VARIABLE, + returnValue.name + ); + + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::RETURN_VAL, + returnName, + valueOperand, + nullptr + )); + + std::cout << " Return value: " << returnValue.name << "\n"; + } + portIndex++; + } + + // Générer le label de sortie + auto exitLabel = std::make_shared( + TACOperand::Type::LABEL, + "exit_" + exitNode->GetExitLabel() + "_" + node->GetId() + ); + + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::LABEL, + exitLabel, + nullptr, + nullptr + )); + + // Générer FUNC_RETURN + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::RETURN, // Utiliser RETURN existant + exitLabel, + nullptr, + nullptr + )); + + // Si c'est la dernière sortie, générer FUNC_END + if (m_currentFunction) { + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::FUNC_END, + nullptr, + nullptr, + nullptr + )); + } + + return exitLabel; + } + + // Génération pour CallFunctionNode + std::shared_ptr GenerateCallFunctionNode(std::shared_ptr node) { + auto* callNode = node->GetAs(); + if (!callNode) return nullptr; + + std::cout << " Generating TAC for CallFunctionNode: " + << callNode->GetFunctionName() << "\n"; + + // 1. Préparer les paramètres + // Récupérer le module appelé pour connaître ses paramètres + // (Ici on suppose qu'on a accès à la liste des paramètres via le module) + + // Pour chaque input binding + const auto& bindings = callNode->GetInputBindings(); + for (size_t i = 0; i < bindings.size(); i++) { + const auto& binding = bindings[i]; + + std::shared_ptr paramValue; + + if (binding.mode == CallFunctionNode::MODE_CONSTANT) { + // Valeur constante + paramValue = std::make_shared( + TACOperand::Type::CONSTANT, + binding.constantValue + ); + } else { + // MODE_CONNECTED : évaluer le nœud connecté + auto portIt = node->dataInputs.find(i + 1); // Port 0 = exec + if (portIt != node->dataInputs.end()) { + paramValue = GenerateNode(portIt->second); + } + } + + if (paramValue) { + // Générer PARAM pour empiler l'argument + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::PARAM, + paramValue, + nullptr, + nullptr + )); + + std::cout << " Param " << i << ": " + << binding.paramName << " = " + << paramValue->ToString() << "\n"; + } + } + + // 2. Générer l'appel + auto functionLabel = std::make_shared( + TACOperand::Type::LABEL, + "function_" + callNode->GetFunctionUuid() + ); + + auto resultTemp = NewTemp(); + + m_program.AddInstruction(std::make_shared( + TACInstruction::OpCode::CALL, + resultTemp, + functionLabel, + nullptr + )); + + std::cout << " Call: " << callNode->GetFunctionName() << "\n"; + + // 3. Le résultat de l'appel est dans resultTemp + // Les valeurs de retour seront accessibles via des LOAD après le CALL + + return resultTemp; + } + // =================================================================== // GÉNÉRATION DU FOR LOOP // =================================================================== @@ -614,7 +836,7 @@ private: auto loopContinue = NewLabel("for_continue"); auto loopEnd = NewLabel("for_end"); - // ⭐ IMPORTANT : Empiler le contexte AVANT de générer le corps + // IMPORTANT : Empiler le contexte AVANT de générer le corps m_loopStack.push_back({ loopStart->GetValue(), loopEnd->GetValue(), @@ -734,7 +956,7 @@ private: ); m_program.AddInstruction(labelEnd); - // ⭐ IMPORTANT : Dépiler APRÈS avoir généré tout le corps + // IMPORTANT : Dépiler APRÈS avoir généré tout le corps m_loopStack.pop_back(); // === 10. COMPLETED (port de sortie 1) === @@ -759,7 +981,7 @@ private: auto loopBody = NewLabel("while_body"); auto loopEnd = NewLabel("while_end"); - // ⭐ Empiler le contexte AVANT le corps + // Empiler le contexte AVANT le corps m_loopStack.push_back({ loopStart->GetValue(), loopEnd->GetValue(), @@ -815,7 +1037,7 @@ private: ); m_program.AddInstruction(labelEnd); - // ⭐ Dépiler APRÈS le corps + // Dépiler APRÈS le corps m_loopStack.pop_back(); // === 8. COMPLETED (port de sortie 1) === @@ -1033,30 +1255,6 @@ private: << (args.size() + 1) << " instructions)\n"; } - void GenerateCallFunctionNode(std::shared_ptr node) { - auto* callNode = node->GetAs(); - if (!callNode) return; - - std::string pageUuid = callNode->GetFunctionUuid(); - std::string pageLabel = "page_" + pageUuid; - - std::cout << " Generating CALL to page: " << pageLabel << "\n"; - - // Créer l'opérande label pour la page cible - auto targetLabel = std::make_shared( - TACOperand::Type::LABEL, - pageLabel - ); - - // Générer l'instruction CALL - auto callInstr = std::make_shared( - TACInstruction::OpCode::CALL, - targetLabel - ); - - m_program.AddInstruction(callInstr); - } - void GenerateBranchNode(std::shared_ptr node) { auto* branchNode = node->GetAs(); if (!branchNode) { @@ -1128,7 +1326,7 @@ private: } // =================================================================== - // NOUVEAUX GÉNÉRATEURS POUR LES NŒUDS SYSCALL + // GÉNÉRATEURS POUR LES NŒUDS SYSCALL // =================================================================== void GenerateWaitDelayNode(std::shared_ptr node) {