forked from eugenechevski/CodeGeneratorSPL
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode_utils.c
More file actions
executable file
·158 lines (146 loc) · 5.87 KB
/
code_utils.c
File metadata and controls
executable file
·158 lines (146 loc) · 5.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// $Id: code_utils.c,v 1.8 2024/11/11 23:01:08 leavens Exp $
#include <assert.h>
#include "regname.h"
#include "code.h"
#include "code_seq.h"
#include "code_utils.h"
#define MINIMAL_STACK_ALLOC_IN_WORDS 4
#define SAVED_SP_OFFSET (-1)
#define SAVED_FP_OFFSET (-2)
#define SAVED_STATIC_LINK_OFFSET (-3)
#define SAVED_RA_OFFSET (-4)
// Requires: t != SP && s != SP
// Return a code sequence that copies the value from register s
// into register t, using the top of the stack as a temporary.
// Modifies: t (and temporarily, SP)
code_seq code_utils_copy_regs(reg_num_type t, reg_num_type s)
{
assert(t != SP);
assert(s != SP);
code_seq ret = code_seq_singleton(code_cpr(t, s));
// If the SSM didn't have the cpr instruction
// one could use the top of the stack as a temporary, as follows
/*
code_seq ret = code_utils_allocate_stack_space(1);
code_seq_add_to_end(&ret, code_swr(SP, 0, s));
code_seq_add_to_end(&ret, code_lwr(t, SP, 0));
code_seq_concat(&ret, code_utils_deallocate_stack_space(1));
*/
return ret;
}
// Return a code sequence that loads the static link
// (which is SAVEDSTATIC_LINK_OFFSET from the address contained in register b,
// and place it in register rt.
// Modifies only register rt.
code_seq code_utils_load_static_link_into_reg(reg_num_type t,
reg_num_type b)
{
code_seq ret = code_seq_singleton(code_lwr(t, b, SAVED_STATIC_LINK_OFFSET));
return ret;
}
// Requires: reg != FP && reg != RA
// Modifies only: register reg
// Return a code sequence that will put the address that corresponds to the
// frame pointer for the given number of scopes outward in register reg
code_seq code_utils_compute_fp(reg_num_type reg, unsigned int levelsOut)
{
assert(reg != FP && reg != RA);
code_seq ret
= code_utils_copy_regs(reg, FP);
while (levelsOut > 0) {
code_seq_concat(&ret,
code_utils_load_static_link_into_reg(reg, reg));
levelsOut--;
}
return ret;
}
// Requires: words >= 0
// Allocate the given number of words on the runtime stack
// Modifies when executed: SP register
// At the end of exeuction, SP holds the address of the last allocated word.
// (So, if the call allocates only one word, then SP addresses that word.)
code_seq code_utils_allocate_stack_space(immediate_type words)
{
assert(words >= 0);
code_seq ret = code_seq_singleton(code_sri(SP, words));
return ret;
}
// Requires: words >= 0
// Deallocate the given number of words from the runtime stack
// Modifies when executed: SP register
code_seq code_utils_deallocate_stack_space(immediate_type words)
{
assert(words >= 0);
code_seq ret = code_seq_singleton(code_ari(SP, words));
return ret;
}
// Requires: $r3 holds the static link (from the current AR)
// Set up the runtime stack for a procedure,
// where the static link is found in register $r3.
// Modifies when executed, the SP register, the FP register,
// and memory from SP to SP - MINIMAL_STACK_ALLOC_IN_WORDS
// (inclusive)
code_seq code_utils_save_registers_for_AR()
{
// assume that SP is pointing to the lowest local storage already allocated
code_seq ret;
// save SP onto the top of the stack
ret = code_seq_singleton(code_swr(SP, SAVED_SP_OFFSET, SP));
// save FP (dynamic link) onto top of the stack
code_seq_add_to_end(&ret, code_swr(SP, SAVED_FP_OFFSET, FP));
// save the static link from $r3 onto top of stack
code_seq_add_to_end(&ret, code_swr(SP, SAVED_STATIC_LINK_OFFSET, 3));
// save the RA register on the stack
code_seq_add_to_end(&ret, code_swr(SP, SAVED_RA_OFFSET, RA));
// save SP into the FP register so FP points to the base of the AR
// can't use code_utils_copy_regs for this because SP is involved
code_seq_add_to_end(&ret, code_cpr(FP, SP));
/* the following can be used if there is no cpr instruction:
code_seq_add_to_end(&ret, SAVED_RA_OFFSET-1, SP));
code_seq_add_to_end(&ret, code_lwr(FP, SP, SAVED_RA_OFFSET-1));
*/
// allocate the space on the stack for the saved registers
code_seq_concat(&ret,
code_utils_allocate_stack_space(MINIMAL_STACK_ALLOC_IN_WORDS));
return ret;
}
// Restore the callee's registers from the places they are saved on the stack.
// This restores the SP, FP, and RA registers only.
// (It is assumed that the stack already holds the static link address)
// Modifies when executed, the SP register, the FP register, the RA register.
// restoring their saved contents from memory
// (as saved by code_utils_save_registers_for_AR)
code_seq code_utils_restore_registers_from_AR()
{
code_seq ret = code_seq_empty();
// restore the RA register
code_seq_add_to_end(&ret,
code_lwr(RA, FP, SAVED_RA_OFFSET));
// save the SP register in $r3 temporarily, to not violate VM's invariant
code_seq_add_to_end(&ret, code_lwr(3, FP, SAVED_SP_OFFSET));
// restore the FP (dynamic link) register
code_seq_add_to_end(&ret, code_lwr(FP, FP, SAVED_FP_OFFSET));
// deallocate the space for variables and constants by restoring old SP
code_seq_add_to_end(&ret, code_cpr(SP, 3));
return ret;
}
// Set up the program's stack as if it were in a call (from the OS)
code_seq code_utils_set_up_program()
{
code_seq ret;
// set up the saved registers
ret = code_utils_copy_regs(3, FP); // save FP into $r3
code_seq_concat(&ret, code_utils_save_registers_for_AR());
// use what will be the new FP as the saved static link
code_seq_add_to_end(&ret, code_swr(FP, SAVED_STATIC_LINK_OFFSET, FP));
return ret;
}
// Tear down the program's stack and exit (with exit code 0).
code_seq code_utils_tear_down_program()
{
code_seq ret;
// restore the saved registers to their initial state
ret = code_utils_restore_registers_from_AR();
code_seq_add_to_end(&ret, code_exit(0));
return ret;
}