first test - function call
This commit is contained in:
561
calling-convention.ipynb
Normal file
561
calling-convention.ipynb
Normal file
@@ -0,0 +1,561 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1c7a11d9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This test will test (lol) the calling convention in Spider.\n",
|
||||
"As defined, it will use the CPU as a python object.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "b9c572cd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The state of the \"my-language-name\" vm in a simple\n",
|
||||
"# python object abstraction\n",
|
||||
"my_language_name_vm = {\n",
|
||||
" 'regs':{\n",
|
||||
" 'RA':0,\n",
|
||||
" 'RB':0,\n",
|
||||
" 'RC':0,\n",
|
||||
" 'RD':0,\n",
|
||||
" 'RX':0,\n",
|
||||
" 'RY':0,\n",
|
||||
" 'R0':0,\n",
|
||||
" 'R1':0,\n",
|
||||
" 'R2':0,\n",
|
||||
" 'R3':0,\n",
|
||||
" 'R4':0,\n",
|
||||
" 'R5':0,\n",
|
||||
" 'R6':0,\n",
|
||||
" 'R7':0,\n",
|
||||
" 'R8':0,\n",
|
||||
" 'R9':0,\n",
|
||||
" 'RF':0,\n",
|
||||
" 'RI':0,\n",
|
||||
" 'RS':0,\n",
|
||||
" 'RZ':0,\n",
|
||||
" 'RE':0,\n",
|
||||
" 'RH':0,\n",
|
||||
" 'RV':0,\n",
|
||||
" 'RM':0,\n",
|
||||
" },\n",
|
||||
" 'stack': [],\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3e3989dc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Test Data Definition**\n",
|
||||
"\n",
|
||||
"This block defines the parameters we want to pass into our hypothetical function and what we expect it to return. We use a mix of large data structures (sizes in bytes) and booleans (size 1) to force the VM to use all of its memory allocation rules."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "a1ddd324",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Input parameters, measured in bytes (booleans are size 1 for logic triggers)\n",
|
||||
"input_params = [\n",
|
||||
" {'name': 'int_a', 'size': 4}, # Should go to RA\n",
|
||||
" {'name': 'bool_1', 'size': 1}, # Should be queued\n",
|
||||
" {'name': 'bool_2', 'size': 1}, # Should be queued\n",
|
||||
" {'name': 'double_b', 'size': 8}, # Should go to RB\n",
|
||||
" {'name': 'big_struct', 'size': 32} # Too large for registers, forces stack usage\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"# Define the return types here\n",
|
||||
"output_return = [ \n",
|
||||
" {'name': 'ret1', 'size': 8} \n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bbe2c356",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now, on this function it will perform the tasks of ordering the registers and stack based on the parameters.\n",
|
||||
"\n",
|
||||
"**The Function Call ABI**\n",
|
||||
"This is the core logic. It prepares the VM for a jump to another function by safely backing up the current context and routing the parameters to either the registers or the stack according to the strict hardware rules.\n",
|
||||
"\n",
|
||||
"**Step 1**: It pushes the caller-saved registers (R0-R3) to the stack so they aren't lost.\n",
|
||||
"\n",
|
||||
"**Step 2**: It checks if the function will return a massive object (> 16 bytes). If so, it reserves a pointer space.\n",
|
||||
"\n",
|
||||
"**Step 3**: It attempts to put parameters into RA, RB, RC, RD, R8, R9. Booleans are packed together to save space.\n",
|
||||
"\n",
|
||||
"**Step 4**: Anything that overflows the registers is pushed to the stack."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "02a2ad1a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO!!!\n",
|
||||
"def do_function_call(input_params: list, output_return: list):\n",
|
||||
" global my_language_name_vm\n",
|
||||
" \n",
|
||||
" print(\"--- 1. Saving Caller State ---\")\n",
|
||||
" # Push caller saved registers (R0, R1, R2, R3)\n",
|
||||
" caller_saved = ['R0', 'R1', 'R2', 'R3']\n",
|
||||
" for reg in caller_saved:\n",
|
||||
" # Guardamos el valor actual del registro\n",
|
||||
" my_language_name_vm['stack'].append(my_language_name_vm['regs'][reg])\n",
|
||||
" \n",
|
||||
" # If the return value is larger than 16 bytes, create space for on the caller's frame\n",
|
||||
" ret_size = sum(r['size'] for r in output_return)\n",
|
||||
" if ret_size > 16:\n",
|
||||
" my_language_name_vm['stack'].append(\"RET_PTR_ALLOCATION\")\n",
|
||||
" \n",
|
||||
" print(\"--- 2. Allocating Parameters ---\")\n",
|
||||
" # Registers RA RB RC RD R8 and R9 can be used to pass parameters\n",
|
||||
" param_regs = ['RA', 'RB', 'RC', 'RD', 'R8', 'R9']\n",
|
||||
" reg_idx = 0\n",
|
||||
" bool_queue = []\n",
|
||||
" stack_params = []\n",
|
||||
" \n",
|
||||
" # Register Allocation Phase\n",
|
||||
" for param in input_params:\n",
|
||||
" if reg_idx < len(param_regs):\n",
|
||||
" if param['size'] == 1:\n",
|
||||
" bool_queue.append(param['name'])\n",
|
||||
" if len(bool_queue) == 8:\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx]] = \"Packed_Bools\"\n",
|
||||
" reg_idx += 1\n",
|
||||
" bool_queue = []\n",
|
||||
" elif param['size'] <= 8:\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx]] = param['name']\n",
|
||||
" reg_idx += 1\n",
|
||||
" elif param['size'] <= 16 and (len(param_regs) - reg_idx) >= 2:\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx]] = f\"{param['name']}_P1\"\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx+1]] = f\"{param['name']}_P2\"\n",
|
||||
" reg_idx += 2\n",
|
||||
" else:\n",
|
||||
" stack_params.append(param)\n",
|
||||
" else:\n",
|
||||
" stack_params.append(param)\n",
|
||||
" \n",
|
||||
" # Flush remaining booleans zero-padded\n",
|
||||
" if len(bool_queue) > 0 and reg_idx < len(param_regs):\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx]] = f\"Padded_Bools({len(bool_queue)})\"\n",
|
||||
" reg_idx += 1\n",
|
||||
" bool_queue = []\n",
|
||||
" \n",
|
||||
" # If no more arguments that fit, and we still have registers left, store the stack pointer into the register, and push it\n",
|
||||
" while reg_idx < len(param_regs):\n",
|
||||
" my_language_name_vm['regs'][param_regs[reg_idx]] = \"SP_Backup\"\n",
|
||||
" my_language_name_vm['stack'].append(f\"SP_for_{param_regs[reg_idx]}\")\n",
|
||||
" reg_idx += 1\n",
|
||||
" \n",
|
||||
" # Stack Allocation Phase\n",
|
||||
" bool_queue = []\n",
|
||||
" large_params = []\n",
|
||||
" \n",
|
||||
" for param in stack_params:\n",
|
||||
" if param['size'] == 1:\n",
|
||||
" bool_queue.append(param['name'])\n",
|
||||
" if len(bool_queue) == 8:\n",
|
||||
" my_language_name_vm['stack'].append(\"Packed_Bools_Stack\")\n",
|
||||
" bool_queue = []\n",
|
||||
" elif param['size'] <= 8:\n",
|
||||
" my_language_name_vm['stack'].append(param['name'])\n",
|
||||
" else:\n",
|
||||
" large_params.append(param)\n",
|
||||
" \n",
|
||||
" if len(bool_queue) > 0:\n",
|
||||
" my_language_name_vm['stack'].append(\"Padded_Bools_Stack\")\n",
|
||||
" \n",
|
||||
" # Finally, add the large parameters in order\n",
|
||||
" for param in large_params:\n",
|
||||
" my_language_name_vm['stack'].append(f\"LARGE_{param['name']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac6440ca",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now, unwind the stack and stuff to make sure the machine goes back to its previous state BEFORE the function\n",
|
||||
"\n",
|
||||
"**The Function Cleanup ABI**\n",
|
||||
"Once the target function finishes executing and returns its answer in the registers, the caller function must clean up the mess it made on the stack before continuing.\n",
|
||||
"\n",
|
||||
"**Step 1**: It pops all the parameters that were pushed to the stack.\n",
|
||||
"\n",
|
||||
"**Step 2**: It pops the backup copies of R0-R3 and puts them back into the actual registers, restoring the caller's state perfectly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "d2e4ce44",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO \n",
|
||||
"def undo_function_call(input_params: list, output_return: list):\n",
|
||||
" global my_language_name_vm\n",
|
||||
" \n",
|
||||
" print(\"--- 3. Cleaning Up Stack ---\")\n",
|
||||
" \n",
|
||||
" # 1. Calcular EXACTAMENTE cuántos elementos se pusieron en la pila (simulando la asignación)\n",
|
||||
" pops_needed = 0\n",
|
||||
" \n",
|
||||
" ret_size = sum(r['size'] for r in output_return)\n",
|
||||
" if ret_size > 16:\n",
|
||||
" pops_needed += 1\n",
|
||||
" \n",
|
||||
" param_regs = ['RA', 'RB', 'RC', 'RD', 'R8', 'R9']\n",
|
||||
" reg_idx = 0\n",
|
||||
" bool_queue = []\n",
|
||||
" stack_params = []\n",
|
||||
" \n",
|
||||
" # Simular qué se fue a registros y qué se fue a la pila\n",
|
||||
" for param in input_params:\n",
|
||||
" if reg_idx < len(param_regs):\n",
|
||||
" if param['size'] == 1:\n",
|
||||
" bool_queue.append(param['name'])\n",
|
||||
" if len(bool_queue) == 8:\n",
|
||||
" reg_idx += 1\n",
|
||||
" bool_queue = []\n",
|
||||
" elif param['size'] <= 8:\n",
|
||||
" reg_idx += 1\n",
|
||||
" elif param['size'] <= 16 and (len(param_regs) - reg_idx) >= 2:\n",
|
||||
" reg_idx += 2\n",
|
||||
" else:\n",
|
||||
" stack_params.append(param)\n",
|
||||
" else:\n",
|
||||
" stack_params.append(param)\n",
|
||||
" \n",
|
||||
" if len(bool_queue) > 0 and reg_idx < len(param_regs):\n",
|
||||
" reg_idx += 1\n",
|
||||
" bool_queue = []\n",
|
||||
" \n",
|
||||
" # Contar los respaldos del Stack Pointer (SP)\n",
|
||||
" while reg_idx < len(param_regs):\n",
|
||||
" pops_needed += 1 \n",
|
||||
" reg_idx += 1\n",
|
||||
" \n",
|
||||
" # Contar los argumentos reales que cayeron en la pila\n",
|
||||
" for param in stack_params:\n",
|
||||
" if param['size'] == 1:\n",
|
||||
" bool_queue.append(param['name'])\n",
|
||||
" if len(bool_queue) == 8:\n",
|
||||
" pops_needed += 1\n",
|
||||
" bool_queue = []\n",
|
||||
" else:\n",
|
||||
" pops_needed += 1 # Tanto <=8 como >8 ocupan 1 espacio en nuestra lista de Python\n",
|
||||
" \n",
|
||||
" if len(bool_queue) > 0:\n",
|
||||
" pops_needed += 1\n",
|
||||
"\n",
|
||||
" # 2. Hacer pop EXACTAMENTE de la cantidad calculada\n",
|
||||
" for _ in range(pops_needed):\n",
|
||||
" if my_language_name_vm['stack']:\n",
|
||||
" removed = my_language_name_vm['stack'].pop()\n",
|
||||
" print(f\"Popped parameter: {removed}\")\n",
|
||||
" \n",
|
||||
" # 3. Restaurar los Caller-Saved registers (siempre son 4)\n",
|
||||
" print(\"--- 4. Restoring Caller Registers ---\")\n",
|
||||
" caller_saved_order = ['R3', 'R2', 'R1', 'R0']\n",
|
||||
" for reg in caller_saved_order:\n",
|
||||
" if my_language_name_vm['stack']:\n",
|
||||
" restored_val = my_language_name_vm['stack'].pop()\n",
|
||||
" my_language_name_vm['regs'][reg] = restored_val\n",
|
||||
" print(f\"Restored {reg} <- {restored_val}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5397697f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"VERY GOOD\n",
|
||||
"Now check the state of the machine before and after\n",
|
||||
"\n",
|
||||
"Try different combinations: One function call, multiple function calls, recursive calls, etc."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "b58fbf72",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--- 1. Saving Caller State ---\n",
|
||||
"--- 2. Allocating Parameters ---\n",
|
||||
"\n",
|
||||
"[STATE AFTER CALL]\n",
|
||||
"Registers: {'RA': 'int_a', 'RB': 'double_b', 'RC': 'Padded_Bools(2)', 'RD': 'SP_Backup', 'R8': 'SP_Backup', 'R9': 'SP_Backup'}\n",
|
||||
"Stack: [0, 0, 0, 0, 'SP_for_RD', 'SP_for_R8', 'SP_for_R9', 'LARGE_big_struct']\n",
|
||||
"\n",
|
||||
"========================================\n",
|
||||
"\n",
|
||||
"--- 3. Cleaning Up Stack ---\n",
|
||||
"Popped parameter: LARGE_big_struct\n",
|
||||
"Popped parameter: SP_for_R9\n",
|
||||
"Popped parameter: SP_for_R8\n",
|
||||
"Popped parameter: SP_for_RD\n",
|
||||
"--- 4. Restoring Caller Registers ---\n",
|
||||
"Restored R3 <- 0\n",
|
||||
"Restored R2 <- 0\n",
|
||||
"Restored R1 <- 0\n",
|
||||
"Restored R0 <- 0\n",
|
||||
"\n",
|
||||
"[STATE AFTER RETURN]\n",
|
||||
"Registers: {'RA': 'int_a', 'RB': 'double_b', 'RC': 'Padded_Bools(2)', 'RD': 'SP_Backup', 'R8': 'SP_Backup', 'R9': 'SP_Backup'}\n",
|
||||
"Stack: []\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Execute the call simulation\n",
|
||||
"do_function_call(input_params, output_return)\n",
|
||||
"\n",
|
||||
"print(\"\\n[STATE AFTER CALL]\")\n",
|
||||
"print(\"Registers:\", {k: v for k, v in my_language_name_vm['regs'].items() if v != 0})\n",
|
||||
"print(\"Stack:\", my_language_name_vm['stack'])\n",
|
||||
"print(\"\\n\" + \"=\"*40 + \"\\n\")\n",
|
||||
"\n",
|
||||
"# Execute the cleanup simulation\n",
|
||||
"undo_function_call(input_params, output_return)\n",
|
||||
"\n",
|
||||
"print(\"\\n[STATE AFTER RETURN]\")\n",
|
||||
"print(\"Registers:\", {k: v for k, v in my_language_name_vm['regs'].items() if v != 0})\n",
|
||||
"print(\"Stack:\", my_language_name_vm['stack'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4f700ac0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Setup and Helper Function**\n",
|
||||
"First, let's create a quick helper to reset the VM so each test starts with a clean slate."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "d225a397",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def reset_vm():\n",
|
||||
" global my_language_name_vm\n",
|
||||
" my_language_name_vm['stack'] = []\n",
|
||||
" for k in my_language_name_vm['regs']:\n",
|
||||
" my_language_name_vm['regs'][k] = 0\n",
|
||||
"\n",
|
||||
"# Standard parameters for testing\n",
|
||||
"test_params = [{'name': 'data', 'size': 8}]\n",
|
||||
"test_returns = [{'name': 'ret', 'size': 8}]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "77df0216",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Test - Sequential Calls**\n",
|
||||
"This tests if the VM can call a function, clean up, and immediately call another function without the stack growing infinitely."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "70347484",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"=== TEST 1: MULTIPLE SEQUENTIAL CALLS ===\n",
|
||||
"[Calling Function A]\n",
|
||||
"--- 1. Saving Caller State ---\n",
|
||||
"--- 2. Allocating Parameters ---\n",
|
||||
"--- 3. Cleaning Up Stack ---\n",
|
||||
"Popped parameter: SP_for_R9\n",
|
||||
"Popped parameter: SP_for_R8\n",
|
||||
"Popped parameter: SP_for_RD\n",
|
||||
"Popped parameter: SP_for_RC\n",
|
||||
"Popped parameter: SP_for_RB\n",
|
||||
"--- 4. Restoring Caller Registers ---\n",
|
||||
"Restored R3 <- 0\n",
|
||||
"Restored R2 <- 0\n",
|
||||
"Restored R1 <- 0\n",
|
||||
"Restored R0 <- 0\n",
|
||||
"\n",
|
||||
"[Calling Function B]\n",
|
||||
"--- 1. Saving Caller State ---\n",
|
||||
"--- 2. Allocating Parameters ---\n",
|
||||
"--- 3. Cleaning Up Stack ---\n",
|
||||
"Popped parameter: SP_for_R9\n",
|
||||
"Popped parameter: SP_for_R8\n",
|
||||
"Popped parameter: SP_for_RD\n",
|
||||
"Popped parameter: SP_for_RC\n",
|
||||
"Popped parameter: SP_for_RB\n",
|
||||
"--- 4. Restoring Caller Registers ---\n",
|
||||
"Restored R3 <- 0\n",
|
||||
"Restored R2 <- 0\n",
|
||||
"Restored R1 <- 0\n",
|
||||
"Restored R0 <- 0\n",
|
||||
"\n",
|
||||
"Final Stack (Should be empty): []\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"=== TEST 1: MULTIPLE SEQUENTIAL CALLS ===\")\n",
|
||||
"reset_vm()\n",
|
||||
"\n",
|
||||
"print(\"[Calling Function A]\")\n",
|
||||
"do_function_call(test_params, test_returns)\n",
|
||||
"undo_function_call(test_params, test_returns)\n",
|
||||
"\n",
|
||||
"print(\"\\n[Calling Function B]\")\n",
|
||||
"do_function_call(test_params, test_returns)\n",
|
||||
"undo_function_call(test_params, test_returns)\n",
|
||||
"\n",
|
||||
"print(\"\\nFinal Stack (Should be empty):\", my_language_name_vm['stack'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "27f37c95",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Test - Recursive / Nested Calls**\n",
|
||||
"This is the most critical test. If Function A calls Function B, the VM must push a second frame onto the stack without destroying Function A's caller-saved registers (R0-R3)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "0c46e3f2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"=== TEST 2: NESTED / RECURSIVE CALLS ===\n",
|
||||
"\n",
|
||||
"[1. Calling Outer Function]\n",
|
||||
"--- 1. Saving Caller State ---\n",
|
||||
"--- 2. Allocating Parameters ---\n",
|
||||
"Outer function modifies R0: IMPORTANT_OUTER_DATA\n",
|
||||
"\n",
|
||||
"[2. Calling Inner Function (Nested)]\n",
|
||||
"--- 1. Saving Caller State ---\n",
|
||||
"--- 2. Allocating Parameters ---\n",
|
||||
"Stack depth during nested call: 18\n",
|
||||
"\n",
|
||||
"[3. Returning from Inner Function]\n",
|
||||
"--- 3. Cleaning Up Stack ---\n",
|
||||
"Popped parameter: SP_for_R9\n",
|
||||
"Popped parameter: SP_for_R8\n",
|
||||
"Popped parameter: SP_for_RD\n",
|
||||
"Popped parameter: SP_for_RC\n",
|
||||
"Popped parameter: SP_for_RB\n",
|
||||
"--- 4. Restoring Caller Registers ---\n",
|
||||
"Restored R3 <- 0\n",
|
||||
"Restored R2 <- 0\n",
|
||||
"Restored R1 <- 0\n",
|
||||
"Restored R0 <- IMPORTANT_OUTER_DATA\n",
|
||||
"Did R0 survive the nested call?: IMPORTANT_OUTER_DATA\n",
|
||||
"\n",
|
||||
"[4. Returning from Outer Function]\n",
|
||||
"--- 3. Cleaning Up Stack ---\n",
|
||||
"Popped parameter: SP_for_R9\n",
|
||||
"Popped parameter: SP_for_R8\n",
|
||||
"Popped parameter: SP_for_RD\n",
|
||||
"Popped parameter: SP_for_RC\n",
|
||||
"Popped parameter: SP_for_RB\n",
|
||||
"--- 4. Restoring Caller Registers ---\n",
|
||||
"Restored R3 <- 0\n",
|
||||
"Restored R2 <- 0\n",
|
||||
"Restored R1 <- 0\n",
|
||||
"Restored R0 <- 0\n",
|
||||
"\n",
|
||||
"Final Stack (Should be empty): []\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"=== TEST 2: NESTED / RECURSIVE CALLS ===\")\n",
|
||||
"reset_vm()\n",
|
||||
"\n",
|
||||
"# 1. We enter the Outer Function\n",
|
||||
"print(\"\\n[1. Calling Outer Function]\")\n",
|
||||
"do_function_call([{'name': 'outer_arg', 'size': 8}], test_returns)\n",
|
||||
"\n",
|
||||
"# Let's simulate the Outer Function doing some math and saving it in R0\n",
|
||||
"my_language_name_vm['regs']['R0'] = \"IMPORTANT_OUTER_DATA\"\n",
|
||||
"print(\"Outer function modifies R0:\", my_language_name_vm['regs']['R0'])\n",
|
||||
"\n",
|
||||
"# 2. Outer Function calls the Inner Function\n",
|
||||
"print(\"\\n[2. Calling Inner Function (Nested)]\")\n",
|
||||
"do_function_call([{'name': 'inner_arg', 'size': 8}], test_returns)\n",
|
||||
"\n",
|
||||
"print(\"Stack depth during nested call:\", len(my_language_name_vm['stack']))\n",
|
||||
"# Notice that \"IMPORTANT_OUTER_DATA\" is now safely backed up inside the stack!\n",
|
||||
"\n",
|
||||
"# 3. Inner Function returns\n",
|
||||
"print(\"\\n[3. Returning from Inner Function]\")\n",
|
||||
"undo_function_call([{'name': 'inner_arg', 'size': 8}], test_returns)\n",
|
||||
"\n",
|
||||
"# 4. Check if the Outer Function's data survived\n",
|
||||
"print(\"Did R0 survive the nested call?:\", my_language_name_vm['regs']['R0'])\n",
|
||||
"\n",
|
||||
"# 5. Outer Function returns\n",
|
||||
"print(\"\\n[4. Returning from Outer Function]\")\n",
|
||||
"undo_function_call([{'name': 'outer_arg', 'size': 8}], test_returns)\n",
|
||||
"\n",
|
||||
"print(\"\\nFinal Stack (Should be empty):\", my_language_name_vm['stack'])"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user