add calling convention implementation

This commit is contained in:
2026-03-04 18:03:38 -06:00
parent 4b93e21441
commit bad91d3b19

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": 15,
"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": 16,
"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": 17,
"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": 18,
"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": 19,
"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": 20,
"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": 21,
"id": "85e645fc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"===== TEST 1: a 1 byte argument =====\n",
"=== State after do_function_call ===\n",
"Registers used: ['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 points to: 4\n",
"=== State after undo_function_call ===\n",
"Clean stack: []\n",
"RS: 0\n",
"Collected result: {'result': {'type': 'result', 'value': 42}}\n",
"\n",
"===== TEST 2: 8 arguments, some on the stack =====\n",
"=== State after do_function_call ===\n",
"Registers used: ['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 points to: 6\n",
"=== State after undo_function_call ===\n",
"Clean stack: []\n",
"RS: 0\n",
"Collected result: {'result': {'type': 'result', 'value': 99}}\n",
"\n",
"===== TEST 3: Boolean arguments =====\n",
"=== State after do_function_call ===\n",
"Registers used: ['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 points to: 4\n",
"=== State after undo_function_call ===\n",
"Clean stack: []\n",
"RS: 0\n",
"Collected result: {'ok': {'type': 'result', 'value': True}}\n"
]
},
{
"data": {
"text/plain": [
"{'ok': {'type': 'result', 'value': True}}"
]
},
"execution_count": 21,
"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
}