3 Commits

Author SHA1 Message Date
1add9f2b20 implementation of do_function_call and undo_function_call 2026-03-03 23:16:41 -06:00
b72746523b ok, i made a change 2026-02-27 12:12:19 -06:00
5690c82e55 first test template 2026-02-27 11:42:39 -06:00
3 changed files with 448 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/spider-tests.code-workspace

View File

@@ -0,0 +1,447 @@
{
"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": 8,
"id": "b9c572cd",
"metadata": {},
"outputs": [],
"source": [
"# The state of the spider vm in a simple\n",
"# python object abstraction\n",
"spider_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": "code",
"execution_count": 9,
"id": "a1ddd324",
"metadata": {},
"outputs": [],
"source": [
"# Input parameters, measured as bits\n",
"# For simplicity\n",
"# 1 boolean -> 1 bit\n",
"# 1 byte -> 8 bits\n",
"input_params = [ {'name':'asd','size':8} ]\n",
"\n",
"# Define the return types here\n",
"ouput_return = [ {'name':'ret1','size':8} ]"
]
},
{
"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."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "02a2ad1a",
"metadata": {},
"outputs": [],
"source": [
"# TODO!!!\n",
"def do_function_call(input_params:list, output_return:list):\n",
" pass"
]
},
{
"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"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "d2e4ce44",
"metadata": {},
"outputs": [],
"source": [
"# TODO !!!\n",
"def undo_function_call(input_params:list, output_return:list):\n",
" pass"
]
},
{
"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": "markdown",
"id": "bcc3aa37",
"metadata": {},
"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": "f01040f2",
"metadata": {},
"outputs": [],
"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": "2e8a479b",
"metadata": {},
"outputs": [],
"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": {
"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,
"nbformat_minor": 5
}

View File

@@ -1 +0,0 @@
Testing my Git configuration for Sintek Analytics.