tac implementation f new nodes function entry and exit with parameters
Some checks are pending
Build-StoryEditor / build_win32 (push) Waiting to run
Build-StoryEditor / build_linux (push) Waiting to run

This commit is contained in:
Anthony Rabine 2025-10-27 14:06:55 +01:00
parent 397da70d83
commit 07849e3e69
2 changed files with 433 additions and 52 deletions

View file

@ -129,6 +129,19 @@ private:
int m_nextTempReg = 0; // Prochain registre t0-t9 disponible int m_nextTempReg = 0; // Prochain registre t0-t9 disponible
int m_tempVarCounter = 0; // Compteur pour variables temporaires en RAM int m_tempVarCounter = 0; // Compteur pour variables temporaires en RAM
// Function call management
struct FunctionCallContext {
int paramCount;
std::string functionLabel;
std::map<std::string, int> paramRegMap;
};
std::shared_ptr<FunctionCallContext> 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 // GÉNÉRATION DE LA SECTION DATA
// =================================================================== // ===================================================================
@ -260,13 +273,35 @@ private:
break; break;
case TACInstruction::OpCode::RETURN: 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; break;
case TACInstruction::OpCode::NOP: case TACInstruction::OpCode::NOP:
m_assembly << " nop\n"; m_assembly << " nop\n";
break; 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: default:
AddComment("WARNING: Unsupported TAC instruction"); AddComment("WARNING: Unsupported TAC instruction");
} }
@ -380,6 +415,173 @@ private:
// GÉNÉRATION DES INSTRUCTIONS // GÉNÉRATION DES INSTRUCTIONS
// =================================================================== // ===================================================================
// Génération pour FUNC_BEGIN
void GenerateFuncBegin(std::shared_ptr<TACInstruction> 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<FunctionCallContext>();
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<TACInstruction> 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<TACInstruction> 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<TACInstruction> 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<TACInstruction> 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<TACInstruction> 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<TACInstruction> 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<TACInstruction> instr) { void GenerateBinaryOp(std::shared_ptr<TACInstruction> instr) {
// Charger les opérandes // Charger les opérandes
LoadOperand(instr->GetOp1(), "r0"); LoadOperand(instr->GetOp1(), "r0");
@ -474,11 +676,6 @@ private:
std::vector<std::shared_ptr<TACOperand>> m_printParams; std::vector<std::shared_ptr<TACOperand>> m_printParams;
void GenerateParam(std::shared_ptr<TACInstruction> instr) {
// Accumuler les paramètres pour le prochain PRINT/CALL
m_printParams.push_back(instr->GetDest());
}
void GeneratePrint(std::shared_ptr<TACInstruction> instr) { void GeneratePrint(std::shared_ptr<TACInstruction> instr) {
// Sauvegarder r0, r1 // Sauvegarder r0, r1
m_assembly << " push r0\n"; m_assembly << " push r0\n";
@ -515,20 +712,6 @@ private:
m_printParams.clear(); m_printParams.clear();
} }
void GenerateCall(std::shared_ptr<TACInstruction> 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 // HELPER : Obtenir la taille d'une variable

View file

@ -113,7 +113,13 @@ public:
// Special // Special
PRINT, // Print (syscall 4) PRINT, // Print (syscall 4)
SYSCALL, // Generic syscall 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) TACInstruction(OpCode op)
@ -252,6 +258,18 @@ public:
case OpCode::SYSCALL: case OpCode::SYSCALL:
ss << "syscall " << m_dest->ToString(); ss << "syscall " << m_dest->ToString();
break; 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: case OpCode::NOP:
ss << "nop"; ss << "nop";
break; break;
@ -328,6 +346,7 @@ private:
#include "while_loop_node.h" #include "while_loop_node.h"
#include "break_node.h" #include "break_node.h"
#include "continue_node.h" #include "continue_node.h"
#include "function_exit_node.h"
// =================================================================== // ===================================================================
// SECTION 2: CLASSE TACGenerator - MODIFICATIONS // SECTION 2: CLASSE TACGenerator - MODIFICATIONS
@ -400,6 +419,17 @@ private:
// Variables disponibles pour résolution // Variables disponibles pour résolution
std::vector<std::shared_ptr<Variable>> m_variables; std::vector<std::shared_ptr<Variable>> m_variables;
// Context de fonction
struct FunctionContext {
std::string functionName;
std::string functionUuid;
std::vector<std::string> parameters; // Noms des paramètres
std::vector<std::string> returnValues; // Noms des valeurs de retour
std::string returnLabel; // Label de sortie
};
std::shared_ptr<FunctionContext> m_currentFunction;
// =================================================================== // ===================================================================
// HELPERS // HELPERS
// =================================================================== // ===================================================================
@ -538,6 +568,12 @@ private:
GenerateNode(node->children[i]); GenerateNode(node->children[i]);
} }
} }
else if (node->IsType<FunctionEntryNode>()) {
return GenerateFunctionEntryNode(node);
}
else if (node->IsType<FunctionExitNode>()) {
return GenerateFunctionExitNode(node);
}
// =================================================================== // ===================================================================
// NŒUDS SYSCALL // NŒUDS SYSCALL
@ -560,7 +596,7 @@ private:
} }
// =================================================================== // ===================================================================
// OUR LES BOUCLES // POUR LES BOUCLES
// =================================================================== // ===================================================================
else if (node->IsType<ForLoopNode>()) { else if (node->IsType<ForLoopNode>()) {
std::cout << " -> Type: ForLoopNode\n"; 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<TACOperand> GenerateFunctionEntryNode(std::shared_ptr<ASTNode> node) {
auto* entryNode = node->GetAs<FunctionEntryNode>();
if (!entryNode) return nullptr;
std::cout << " Generating TAC for FunctionEntryNode\n";
// Créer un contexte de fonction
m_currentFunction = std::make_shared<FunctionContext>();
m_currentFunction->functionName = "function_" + node->GetId();
// Générer FUNC_BEGIN
auto funcLabel = std::make_shared<TACOperand>(
TACOperand::Type::LABEL,
m_currentFunction->functionName
);
m_program.AddInstruction(std::make_shared<TACInstruction>(
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>(
TACOperand::Type::VARIABLE,
param.name
);
m_program.AddInstruction(std::make_shared<TACInstruction>(
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<TACOperand> GenerateFunctionExitNode(std::shared_ptr<ASTNode> node) {
auto* exitNode = node->GetAs<FunctionExitNode>();
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>(
TACOperand::Type::VARIABLE,
returnValue.name
);
m_program.AddInstruction(std::make_shared<TACInstruction>(
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>(
TACOperand::Type::LABEL,
"exit_" + exitNode->GetExitLabel() + "_" + node->GetId()
);
m_program.AddInstruction(std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
exitLabel,
nullptr,
nullptr
));
// Générer FUNC_RETURN
m_program.AddInstruction(std::make_shared<TACInstruction>(
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>(
TACInstruction::OpCode::FUNC_END,
nullptr,
nullptr,
nullptr
));
}
return exitLabel;
}
// Génération pour CallFunctionNode
std::shared_ptr<TACOperand> GenerateCallFunctionNode(std::shared_ptr<ASTNode> node) {
auto* callNode = node->GetAs<CallFunctionNode>();
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<TACOperand> paramValue;
if (binding.mode == CallFunctionNode::MODE_CONSTANT) {
// Valeur constante
paramValue = std::make_shared<TACOperand>(
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>(
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>(
TACOperand::Type::LABEL,
"function_" + callNode->GetFunctionUuid()
);
auto resultTemp = NewTemp();
m_program.AddInstruction(std::make_shared<TACInstruction>(
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 // GÉNÉRATION DU FOR LOOP
// =================================================================== // ===================================================================
@ -614,7 +836,7 @@ private:
auto loopContinue = NewLabel("for_continue"); auto loopContinue = NewLabel("for_continue");
auto loopEnd = NewLabel("for_end"); 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({ m_loopStack.push_back({
loopStart->GetValue(), loopStart->GetValue(),
loopEnd->GetValue(), loopEnd->GetValue(),
@ -734,7 +956,7 @@ private:
); );
m_program.AddInstruction(labelEnd); 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(); m_loopStack.pop_back();
// === 10. COMPLETED (port de sortie 1) === // === 10. COMPLETED (port de sortie 1) ===
@ -759,7 +981,7 @@ private:
auto loopBody = NewLabel("while_body"); auto loopBody = NewLabel("while_body");
auto loopEnd = NewLabel("while_end"); auto loopEnd = NewLabel("while_end");
// Empiler le contexte AVANT le corps // Empiler le contexte AVANT le corps
m_loopStack.push_back({ m_loopStack.push_back({
loopStart->GetValue(), loopStart->GetValue(),
loopEnd->GetValue(), loopEnd->GetValue(),
@ -815,7 +1037,7 @@ private:
); );
m_program.AddInstruction(labelEnd); m_program.AddInstruction(labelEnd);
// Dépiler APRÈS le corps // Dépiler APRÈS le corps
m_loopStack.pop_back(); m_loopStack.pop_back();
// === 8. COMPLETED (port de sortie 1) === // === 8. COMPLETED (port de sortie 1) ===
@ -1033,30 +1255,6 @@ private:
<< (args.size() + 1) << " instructions)\n"; << (args.size() + 1) << " instructions)\n";
} }
void GenerateCallFunctionNode(std::shared_ptr<ASTNode> node) {
auto* callNode = node->GetAs<CallFunctionNode>();
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>(
TACOperand::Type::LABEL,
pageLabel
);
// Générer l'instruction CALL
auto callInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::CALL,
targetLabel
);
m_program.AddInstruction(callInstr);
}
void GenerateBranchNode(std::shared_ptr<ASTNode> node) { void GenerateBranchNode(std::shared_ptr<ASTNode> node) {
auto* branchNode = node->GetAs<BranchNode>(); auto* branchNode = node->GetAs<BranchNode>();
if (!branchNode) { 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<ASTNode> node) { void GenerateWaitDelayNode(std::shared_ptr<ASTNode> node) {