I am working on the Simple-Compiler project in Deitel's book C how to program. Its main goal is to generate a compiler for an advanced language called SIMPLE and the relevant machine language is called SIMPLETRON.
I've completed some basic features for this compiler but am now stuck with an enhanced requirement -- to realize gosub and return (subroutine features) for SIMPLE language.
The main obstacle here is that SIMPLETRON doesn't support indirect addressing, which means the strategy to use stack for returning addresses of subroutines can't work. In this case, is it possible to somehow make subroutines work?
PS: I searched this issue and found an relevant question here. It seemed self-modifying code might be the answer, but I failed to find specific resolutions and thus I still raised this question. Moreover in my opinion machine instructions for SIMPLETRON has to be extended to make self-modifying code work here, right?
Background information for SIMPLETRON machine language:
- It includes only one accumulator as register.
- All supported machine instructions as below:
- Input/output operations
#define READ 10
: Read a word from the terminal into memory and with an operand as the memory address.#define WRITE 11
: Write a word from memory to the terminal and with an operand as the memory address.- Load/store operations
#define LOAD 20
: Load a word from memory into the accumulator and with an operand as the memory address.#define STORE 21
: Store a word from the accumulator into memory and with an operand as the memory address.- Arithmetic operations
#define ADD 30
: Add a word from memory to the word in the accumulator (leave result in accumulator) and with an operand as the memory address.#define SUBTRACT 31
: Subtract a word ...#define DIVIDE 32
: Divide a word ...#define MULTIPLY 33
: Multiply a word ...- Transfer of control operations
#define BRANCH 40
: Branch and with an operand as the code location.#define BRANCHNEG 41
: Branch if the accumulator is negative and with an operand as the code location.#define BRANCHZERO 42
: Branch if the accumulator is zero and with an operand as the code location.#define HALT 43
: End the program. No operand.
I'm not familiar with SIMPLE or SIMPLETRON, but in general I can think of at least 3 approaches.
Self-modifying code
Have a
BRANCH 0
instruction at the end of each subroutine, and before that, code to load the return address into the accumulator andSTORE
it into the code itself, thus effectively forming aBRANCH <dynamic>
instruction.Static list of potential callers
If SIMPLE doesn't have indirect calls (i.e. every
gosub
targets a statically known subroutine), then the compiler knows the list of possible callers of each subroutine. Then it could have each call pass a unique argument (e.g. in the accumulator), which the subroutine can test (pseudocode):Inlining
If SIMPLE doesn't allow recursive subroutines, there's no need to implement calls at the machine code level at all. Simply inline every subroutine into its caller completely.