1 Stack format / Calling convention
2 ---------------------------------
4 We are about to call a function.
5 fp at the time of the activation is equal to the current stack pointer plus the function frame size
8 stack[fp-6] -> the stack pointer before the stack allocation for this frame + localvars + registers is done
9 stack[fp-5] -> the absolute stack position (stack location) of where the callee should place its result
10 stack[fp-4] -> the code pointer (ip) of the caller
11 stack[fp-3] -> the callee's closure/method
12 stack[fp-2] -> the callee's lexical context (nil for stack allocated frames)
13 stack[fp-1] -> frame pointer of caller
14 stack[fp+0] .. stack[fp+registerCount+lvCount] -> registers + local variables
16 When you return from a function and you want to restore the frame in
17 the interpreter object to what it was before the call, you restore
18 everything based on fp (interpreter's current fp) except the closure
19 and the lexical context which are restored based on the saved
20 framepointer (at fp-1) using the previous frame (notice callee
24 basic overview (code summary):
26 function calling -- interpreter_apply_to_arity_with_optionals():
28 framePointer = i->stackPointer + FUNCTION_FRAME_SIZE;
29 /* store the old stack pointer so we know what to return it to after this function ends */
30 beforeCallStackPointer = i->stackPointer;
31 interpreter_stack_allocate(oh, i, FUNCTION_FRAME_SIZE /*frame size in words*/ + object_to_smallint(method->localVariables) + object_to_smallint(method->registerCount));
32 i->stack->elements[framePointer - 6] = smallint_to_object(beforeCallStackPointer);
33 i->stack->elements[framePointer - 5] = smallint_to_object(resultStackPointer);
34 i->stack->elements[framePointer - 4] = smallint_to_object(i->codePointer);
35 i->stack->elements[framePointer - 3] = (struct Object*) closure;
36 i->stack->elements[framePointer - 2] = (struct Object*) lexicalContext;
37 i->stack->elements[framePointer - 1] = smallint_to_object(i->framePointer);
38 i->framePointer = framePointer;
41 i->lexicalContext = lexicalContext;
44 function returning -- interpreter_return_result()
47 framePointer = i->framePointer;
48 resultStackPointer = (word_t)i->stack->elements[framePointer - 5]>>1;
49 i->stack->elements[resultStackPointer] = result;
50 i->stackPointer = object_to_smallint(i->stack->elements[framePointer - 6]);
51 i->framePointer = object_to_smallint(i->stack->elements[framePointer - 1]);
52 i->codePointer = object_to_smallint(i->stack->elements[framePointer - 4]);
53 i->lexicalContext = (struct LexicalContext*) i->stack->elements[i->framePointer - 2];
54 i->closure = (struct Closure*) i->stack->elements[i->framePointer - 3];
55 i->method = i->closure->method;
56 i->codeSize = array_size(i->method->code);
72 method sourceTree result: Nil &topLevel: True) code.
74 ] applyWith: VM SSACompiler new.
78 Inside a function definition:
85 block@(Method traits) on: c@(Condition traits) do: handler
87 context: (c cloneSettingSlots: #(handlers exitContinuation)
88 to: {{handler}. [| :result | ^ result]}).
89 conditionStack push: context.
90 block ensure: [conditionStack pop]
94 method sourceTree result: Nil &topLevel: True) code.
95 c decompile: genCode third code
96 ] applyWith: VM SSACompiler new.
105 genCode: (c generate:
107 block@(Method traits) on: c@(Condition traits) do: handler
109 context: (c cloneSettingSlots: #(handlers exitContinuation)
110 to: {{handler}. [| :result | ^ result]}).
111 conditionStack push: context.
112 block ensure: [conditionStack pop]
116 method sourceTree result: Nil &topLevel: True) code.
117 c decompile: (genCode third code at: 13) code
118 ] applyWith: VM SSACompiler new.
122 See what is getting called:
126 VM SSACompiler new decompile: (#handle: findOn: {mainWindow. PaintEvent newContext: mainWindow context}) method code.