Compare commits
3 Commits
test-setup
...
lopez/call
| Author | SHA1 | Date | |
|---|---|---|---|
| 1add9f2b20 | |||
| b72746523b | |||
| 5690c82e55 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/spider-tests.code-workspace
|
||||||
447
calling-convention/calling-convention.ipynb
Normal file
447
calling-convention/calling-convention.ipynb
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user