extended the extern support

This commit is contained in:
igor
2026-02-26 11:01:38 +01:00
parent c5cdd5a7fd
commit 95cf89cdf5

78
main.py
View File

@@ -4002,40 +4002,82 @@ class Assembler:
int_idx = 0 int_idx = 0
xmm_idx = 0 xmm_idx = 0
mapping: List[Tuple[str, str]] = [] # (type, target_reg) mapping: List[Tuple[str, str]] = [] # (type, target)
# Assign registers for first args; overflow goes to stack
if not arg_types: if not arg_types:
# Legacy/Raw mode: assume all ints # Legacy/Raw mode: assume all ints
if inputs > 6:
raise CompileError(f"extern '{name}' has too many inputs ({inputs} > 6)")
for i in range(inputs): for i in range(inputs):
mapping.append(("int", regs[i])) if int_idx < len(regs):
mapping.append(("int", regs[int_idx]))
int_idx += 1
else:
mapping.append(("int", "stack"))
else: else:
for type_name in arg_types: for type_name in arg_types:
if type_name in ("float", "double"): if type_name in ("float", "double"):
if xmm_idx >= 8: if xmm_idx < len(xmm_regs):
raise CompileError(f"extern '{name}' has too many float inputs")
mapping.append(("float", xmm_regs[xmm_idx])) mapping.append(("float", xmm_regs[xmm_idx]))
xmm_idx += 1 xmm_idx += 1
else: else:
if int_idx >= 6: mapping.append(("float", "stack"))
raise CompileError(f"extern '{name}' has too many int inputs") else:
if int_idx < len(regs):
mapping.append(("int", regs[int_idx])) mapping.append(("int", regs[int_idx]))
int_idx += 1 int_idx += 1
for type_name, reg in reversed(mapping):
if type_name == "float":
builder.pop_to("rax")
builder.emit(f" movq {reg}, rax")
else: else:
builder.pop_to(reg) mapping.append(("int", "stack"))
builder.emit(" push rbp") # Count stack slots required
builder.emit(" mov rbp, rsp") stack_slots = sum(1 for t, target in mapping if target == "stack")
builder.emit(" and rsp, -16") # stack allocation in bytes; make it a multiple of 16 for alignment
stack_bytes = ((stack_slots * 8 + 15) // 16) * 16 if stack_slots > 0 else 0
# Prepare stack-passed arguments: allocate space (16-byte multiple)
if stack_bytes:
builder.emit(f" sub rsp, {stack_bytes}")
# Read all arguments from the CT stack by indexed addressing
# (without advancing r12) and write them to registers or the
# prepared spill area. After all reads are emitted we advance
# r12 once by the total number of arguments to pop them.
total_args = len(mapping)
if stack_slots:
stack_write_idx = stack_slots - 1
else:
stack_write_idx = 0
# Iterate over reversed mapping (right-to-left) but use an
# index to address the CT stack without modifying r12.
for idx, (typ, target) in enumerate(reversed(mapping)):
addr = f"[r12 + {idx * 8}]" if idx > 0 else "[r12]"
if target == "stack":
# Read spilled arg from indexed CT stack slot and store
# it into the caller's spill area at the computed offset.
builder.emit(f" mov rax, {addr}")
offset = stack_write_idx * 8
builder.emit(f" mov [rsp + {offset}], rax")
stack_write_idx -= 1
else:
if typ == "float":
builder.emit(f" mov rax, {addr}")
builder.emit(f" movq {target}, rax")
else:
builder.emit(f" mov {target}, {addr}")
# Advance the CT stack pointer once to pop all arguments.
if total_args:
builder.emit(f" add r12, {total_args * 8}")
# Call the external function. We allocated a multiple-of-16
# area for spilled args above so `rsp` is already aligned
# for the call; set `al` (SSE count) then call directly.
builder.emit(f" mov al, {xmm_idx}") builder.emit(f" mov al, {xmm_idx}")
builder.emit(f" call {name}") builder.emit(f" call {name}")
builder.emit(" leave")
# Restore stack after the call
if stack_bytes:
builder.emit(f" add rsp, {stack_bytes}")
# Handle Return Value # Handle Return Value
if _ctype_uses_sse(ret_type): if _ctype_uses_sse(ret_type):