implementation of do_function_call and undo_function_call

This commit is contained in:
2026-03-03 23:16:41 -06:00
parent b72746523b
commit 1add9f2b20

View File

@@ -11,7 +11,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 8,
"id": "b9c572cd",
"metadata": {},
"outputs": [],
@@ -51,7 +51,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 9,
"id": "a1ddd324",
"metadata": {},
"outputs": [],
@@ -76,7 +76,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 10,
"id": "02a2ad1a",
"metadata": {},
"outputs": [],
@@ -96,7 +96,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 11,
"id": "d2e4ce44",
"metadata": {},
"outputs": [],
@@ -118,33 +118,328 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b58fbf72",
"cell_type": "markdown",
"id": "bcc3aa37",
"metadata": {},
"outputs": [],
"source": []
"source": [
"## Implementing do_function_call\n",
"This function simulates the function call process following Spider's calling convention.\n",
"It places arguments into registers (RA, RB, RC, RD, R8, R9), and anything that doesn't fit is placed on the stack.\n",
"Booleans are bundled together before being placed in a register."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2860c30b",
"id": "f01040f2",
"metadata": {},
"outputs": [],
"source": []
"source": [
"def do_function_call(input_params: list, output_return: list):\n",
" \n",
" # Rgisters available for arguments in order\n",
" arg_registers = ['RA', 'RB', 'RC', 'RD', 'R8', 'R9']\n",
" reg_index = 0 # index of current register\n",
" \n",
" # Save caller saved registers to the stack\n",
"\n",
" for reg in ['R0', 'R1', 'R2', 'R3']:\n",
" spider_vm['stack'].append({\n",
" 'type': 'caller_saved',\n",
" 'reg': reg,\n",
" 'value': spider_vm['regs'][reg]\n",
" })\n",
" \n",
" # If the return value is greater than 16 bytes (128 bits),\n",
" # reserve space on the stack and append a pointer to the front.\n",
" \n",
" total_return_size = sum(p['size'] for p in output_return)\n",
" if total_return_size > 128:\n",
" spider_vm['stack'].append({\n",
" 'type': 'return_space',\n",
" 'size': total_return_size,\n",
" 'value': None\n",
" })\n",
" # The pointer to the return space goes at the beginning of the arguments\n",
" input_params = [{'name': '__return_ptr', 'size': 64}] + input_params\n",
" \n",
" # Arrange arguments in registers and stack\n",
" \n",
" bool_queue = [] # Boolean accumulator (1 bit each)\n",
" stack_params = [] # Arguments that didn't fit in records\n",
" \n",
" for param in input_params:\n",
" is_bool = param['size'] == 1\n",
" fits_in_register = param['size'] <= 128 # Up to 16 bytes = 128 bits\n",
" \n",
" if is_bool:\n",
" # Booleans accumulate together\n",
" bool_queue.append(param)\n",
" \n",
" # If the boolean queue forms 8 bytes (64 bits), it is sent\n",
" if sum(p['size'] for p in bool_queue) >= 64:\n",
" if reg_index < len(arg_registers):\n",
" spider_vm['regs'][arg_registers[reg_index]] = {\n",
" 'type': 'bool_pack',\n",
" 'params': bool_queue.copy()\n",
" }\n",
" reg_index += 1\n",
" else:\n",
" stack_params.append({\n",
" 'type': 'bool_pack',\n",
" 'params': bool_queue.copy()\n",
" })\n",
" bool_queue = []\n",
" \n",
" elif fits_in_register and reg_index < len(arg_registers):\n",
" # It fits in the register and register is available.\n",
" spider_vm['regs'][arg_registers[reg_index]] = {\n",
" 'type': 'param',\n",
" 'name': param['name'],\n",
" 'size': param['size'],\n",
" 'value': 0 \n",
" }\n",
" reg_index += 1\n",
" \n",
" else:\n",
" # It doesn't fit in the register, it goes to the stack later\n",
" stack_params.append(param)\n",
" \n",
" # If there are any pending booleans and a register is available\n",
" if bool_queue:\n",
" if reg_index < len(arg_registers):\n",
" spider_vm['regs'][arg_registers[reg_index]] = {\n",
" 'type': 'bool_pack_padded',\n",
" 'params': bool_queue.copy()\n",
" }\n",
" reg_index += 1\n",
" else:\n",
" stack_params.append({\n",
" 'type': 'bool_pack_padded',\n",
" 'params': bool_queue.copy()\n",
" })\n",
" bool_queue = []\n",
" \n",
" # Push onto the stack what didn't fit in the registers\n",
" # First the small ones (up to 8 bytes = 64 bits), then the large ones\n",
"\n",
" small_params = [p for p in stack_params if p.get('size', 999) <= 64]\n",
" large_params = [p for p in stack_params if p.get('size', 0) > 64]\n",
" \n",
" for param in small_params:\n",
" spider_vm['stack'].append({\n",
" 'type': 'param',\n",
" 'name': param.get('name', 'bool_pack'),\n",
" 'size': param.get('size'),\n",
" 'value': 0\n",
" })\n",
" \n",
" for param in large_params:\n",
" spider_vm['stack'].append({\n",
" 'type': 'large_param',\n",
" 'name': param['name'],\n",
" 'size': param['size'],\n",
" 'value': 0\n",
" })\n",
" \n",
" # Update RS to reflect the top of the stack\n",
" spider_vm['regs']['RS'] = len(spider_vm['stack'])\n",
" \n",
" print(\"=== State after do_function_call ===\")\n",
" print(f\"Registers used: {arg_registers[:reg_index]}\")\n",
" print(f\"Stack: {spider_vm['stack']}\")\n",
" print(f\"RS points to: {spider_vm['regs']['RS']}\")"
]
},
{
"cell_type": "markdown",
"id": "962c406c",
"metadata": {},
"source": [
"## Implementing undo_function_call\n",
"This function restores the VM's state to its state before the call.\n",
"It collects the RA result, cleans the stack, and restores the caller-saved records."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2aa8a8fc",
"id": "2e8a479b",
"metadata": {},
"outputs": [],
"source": []
"source": [
"def undo_function_call(input_params: list, output_return: list):\n",
" \n",
" # Collect results from return registers\n",
" # Up to 16 bytes (128 bits) reside in RA, RB, RC, RD\n",
" # ─────────────────────────────────────────\n",
" total_return_size = sum(p['size'] for p in output_return)\n",
" return_registers = ['RA', 'RB', 'RC', 'RD']\n",
" result = {}\n",
" \n",
" if total_return_size <= 128:\n",
" regs_needed = (total_return_size + 31) // 32 # How many registers do we need\n",
" for i, param in enumerate(output_return):\n",
" if i < len(return_registers):\n",
" result[param['name']] = spider_vm['regs'][return_registers[i]]\n",
" else:\n",
" # The result was in the reserved space of the stack.\n",
" for entry in spider_vm['stack']:\n",
" if entry.get('type') == 'return_space':\n",
" result['large_return'] = entry['value']\n",
" break\n",
" \n",
" # Clean the argument stack and return space\n",
" \n",
" spider_vm['stack'] = [\n",
" entry for entry in spider_vm['stack']\n",
" if entry.get('type') == 'caller_saved'\n",
" ]\n",
" \n",
" # # Restore caller saved registers in reverse order\n",
" \n",
" for entry in reversed(spider_vm['stack'].copy()):\n",
" if entry['type'] == 'caller_saved':\n",
" spider_vm['regs'][entry['reg']] = entry['value']\n",
" \n",
" # Clean caller_saved entries from the stack\n",
" spider_vm['stack'] = []\n",
" \n",
" # Update RS\n",
" spider_vm['regs']['RS'] = len(spider_vm['stack'])\n",
" \n",
" print(\"=== State after undo_function_call ===\")\n",
" print(f\"Clean stack: {spider_vm['stack']}\")\n",
" print(f\"RS: {spider_vm['regs']['RS']}\")\n",
" print(f\"Collected result: {result}\")\n",
" return result"
]
},
{
"cell_type": "markdown",
"id": "2b76da93",
"metadata": {},
"source": [
"## Test Cases\n",
"Three cases are tested: a single argument, multiple arguments that overflow the registers, and boolean arguments."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "85e645fc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"===== PRUEBA 1: un argumento de 1 byte =====\n",
"=== Estado después de do_function_call ===\n",
"Registros usados: ['RA']\n",
"Stack: [{'type': 'caller_saved', 'reg': 'R0', 'value': 0}, {'type': 'caller_saved', 'reg': 'R1', 'value': 0}, {'type': 'caller_saved', 'reg': 'R2', 'value': 0}, {'type': 'caller_saved', 'reg': 'R3', 'value': 0}]\n",
"RS apunta a: 4\n",
"=== Estado después de undo_function_call ===\n",
"Stack limpio: []\n",
"RS: 0\n",
"Resultado recogido: {'resultado': {'type': 'result', 'value': 42}}\n",
"\n",
"===== PRUEBA 2: 8 argumentos, algunos al stack =====\n",
"=== Estado después de do_function_call ===\n",
"Registros usados: ['RA', 'RB', 'RC', 'RD', 'R8', 'R9']\n",
"Stack: [{'type': 'caller_saved', 'reg': 'R0', 'value': 0}, {'type': 'caller_saved', 'reg': 'R1', 'value': 0}, {'type': 'caller_saved', 'reg': 'R2', 'value': 0}, {'type': 'caller_saved', 'reg': 'R3', 'value': 0}, {'type': 'param', 'name': 'g', 'size': 64, 'value': 0}, {'type': 'param', 'name': 'h', 'size': 64, 'value': 0}]\n",
"RS apunta a: 6\n",
"=== Estado después de undo_function_call ===\n",
"Stack limpio: []\n",
"RS: 0\n",
"Resultado recogido: {'resultado': {'type': 'result', 'value': 99}}\n",
"\n",
"===== PRUEBA 3: argumentos booleanos =====\n",
"=== Estado después de do_function_call ===\n",
"Registros usados: ['RA', 'RB']\n",
"Stack: [{'type': 'caller_saved', 'reg': 'R0', 'value': 0}, {'type': 'caller_saved', 'reg': 'R1', 'value': 0}, {'type': 'caller_saved', 'reg': 'R2', 'value': 0}, {'type': 'caller_saved', 'reg': 'R3', 'value': 0}]\n",
"RS apunta a: 4\n",
"=== Estado después de undo_function_call ===\n",
"Stack limpio: []\n",
"RS: 0\n",
"Resultado recogido: {'ok': {'type': 'result', 'value': True}}\n"
]
},
{
"data": {
"text/plain": [
"{'ok': {'type': 'result', 'value': True}}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# TEST 1: A single function with one argument\n",
"\n",
"print(\"\\n===== TEST 1: a 1 byte argument =====\")\n",
"input_params = [{'name': 'x', 'size': 8}]\n",
"output_return = [{'name': 'result', 'size': 8}]\n",
"\n",
"do_function_call(input_params, output_return)\n",
"spider_vm['regs']['RA'] = {'type': 'result', 'value': 42} # Simulate return\n",
"undo_function_call(input_params, output_return)\n",
"\n",
"# TEST 2: Multiple arguments, some go on the stack\n",
"\n",
"print(\"\\n===== TEST 2: 8 arguments, some on the stack =====\")\n",
"input_params = [\n",
" {'name': 'a', 'size': 8},\n",
" {'name': 'b', 'size': 16},\n",
" {'name': 'c', 'size': 32},\n",
" {'name': 'd', 'size': 8},\n",
" {'name': 'e', 'size': 8},\n",
" {'name': 'f', 'size': 8},\n",
" {'name': 'g', 'size': 64}, # This will go on the stack\n",
" {'name': 'h', 'size': 64}, # this one too\n",
"]\n",
"output_return = [{'name': 'result', 'size': 32}]\n",
"\n",
"do_function_call(input_params, output_return)\n",
"spider_vm['regs']['RA'] = {'type': 'result', 'value': 99}\n",
"undo_function_call(input_params, output_return)\n",
"\n",
"# TEST 3: Booleans\n",
"\n",
"print(\"\\n===== TEST 3: Boolean arguments =====\")\n",
"input_params = [\n",
" {'name': 'flag1', 'size': 1},\n",
" {'name': 'flag2', 'size': 1},\n",
" {'name': 'flag3', 'size': 1},\n",
" {'name': 'x', 'size': 8},\n",
"]\n",
"output_return = [{'name': 'ok', 'size': 1}]\n",
"do_function_call(input_params, output_return)\n",
"spider_vm['regs']['RA'] = {'type': 'result', 'value': True}\n",
"undo_function_call(input_params, output_return)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.5"
}
},
"nbformat": 4,