Oct 5, 2004 - C source files produce an executable file that can execute on the DSP. The only .... The AGU performs all
Retargeting a C Compiler for a DSP Processor Master thesis performed in electronics systems by
Henrik Antelius LiTH-ISY-EX-3595-2004 Linköping 2004
Retargeting a C Compiler for a DSP Processor Master thesis in electronics systems at Linköping Institute of Technology by
Henrik Antelius LiTH-ISY-EX-3595-2004
Supervisors: Thomas Johansson Ulrik Lindblad Patrik Thalin Examiner:
Kent Palmkvist
Linköping, 2004-10-05
Avdelning, Institution Division, Department
Datum Date 2004-10-05
Institutionen för systemteknik 581 83 LINKÖPING Språk Language Svenska/Swedish X Engelska/English
Rapporttyp Report category Licentiatavhandling X Examensarbete C-uppsats D-uppsats
ISBN ISRN LITH-ISY-EX-3595-2004 Serietitel och serienummer Title of series, numbering
ISSN
Övrig rapport ____
URL för elektronisk version http://www.ep.liu.se/exjobb/isy/2004/3595/ Titel Title
Anpassning av en C-kompilator för kodgenerering till en DSP-processor Retargeting a C Compiler for a DSP Processor
Författare Author
Henrik Antelius
Sammanfattning Abstract The purpose of this thesis is to retarget a C compiler for a DSP processor. Developing a new compiler from scratch is a major task. Instead, modifying an existing compiler so that it generates code for another target is a common way to develop compilers for new processors. This is called retargeting. This thesis describes how this was done with the LCC C compiler for the Motorola DSP56002 processor.
Nyckelord Keyword retarget, compiler, LCC, DSP
Abstract The purpose of this thesis is to retarget a C compiler for a DSP processor. Developing a new compiler from scratch is a major task. Instead, modifying an existing compiler so that it generates code for another target is a common way to develop compilers for new processors. This is called retargeting. This thesis describes how this was done with the LCC C compiler for the Motorola DSP56002 processor.
Table of contents
1 Introduction 1.1 1.2 1.3 1.4
Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Purpose and goal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The reader. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reading guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 DSP 2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Motorola DSP56002. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Data buses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Address buses. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.3 Data ALU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.4 Address generation unit . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.5 Program control unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Instruction set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 Compilers
1 1 1 2 2
3 3 4 5 5 5 5 6 6 6
9
3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 3.2 The analysis-synthesis model . . . . . . . . . . . . . . . . . . . . . . . . 9 3.3 Phases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.4 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.4.1 Lexical analysis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.4.2 Syntax analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.4.3 Semantic analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.5 Synthesis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.5.1 Intermediate code generation . . . . . . . . . . . . . . . . . . . 15 3.5.2 Code optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.5.3 Code generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.6 Symbol table. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.7 Error handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.8 Front and back end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.9 Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 ix
3.9.1 Preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.9.2 Assembler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.9.3 Linker and loader. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.10 Compiler tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 LCC 4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 The compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Lexical analysis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.2 Syntax analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.3 Semantic analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.4 Intermediate code generation . . . . . . . . . . . . . . . . . . . 4.3.5 Back end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5 Implementation 5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 The compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 Data types and sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.2 Register usage. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.3 Memory usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.4 Frame layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.5 Calling convention. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.6 Naming convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Retargeting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.3 Rules. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.4 C code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4 Special features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.5 Other changes to LCC. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.6 The environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.7 crt0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.8 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.8.1 Register targeting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.8.2 48-bit registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.8.3 Address registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.9 Improvements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6 Conclusions
20 20 21 21
23 23 24 24 24 26 29 29 30
33 33 33 34 35 36 37 38 39 39 40 40 40 40 44 45 45 46 46 46 48 48 49
51
6.1 Retargeting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 6.2 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
References x
53
Table of contents
Appendix A: Instructions A.1 A.2 A.3 A.4 A.5 A.6
Arithmetic instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . Logical instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bit manipulation instructions . . . . . . . . . . . . . . . . . . . . . . . Loop instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Move instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Program control instructions . . . . . . . . . . . . . . . . . . . . . . .
Appendix B: Sample code
55 55 56 57 57 57 58
59
B.1 sample.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 B.2 sample.asm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Appendix C: dsp56k.md
61
Index
79
xi
xii
1 Introduction 1.1 Background The division of Electronics Systems (ES) at the department of Electrical Engineering (ISY) at Linköping University (LiU) is currently running a project aiming at developing a DSP processor. The goal of this project is to make a DSP with a scalable structure that is instruction level compatible with the Motorola DSP56002 processor. The scalability refers to variable data word length and addition or removal of memories and instructions. The goal with scalability is to reduce the power consumption. Currently this project is nearly finished. In order to increase the usability of the DSP a C compiler is needed. It was decided that the best way to create a C compiler was to retarget an existing C compiler. Creating a compiler from scratch is a big undertaking that requires a lot of work. Retargeting a compiler is a relatively easy task compared to developing an entire compiler.
1.2 Purpose and goal The purpose of this thesis is to retarget a C compiler to the Motorola DSP56002 processor. The resulting compiler should from one or more C source files produce an executable file that can execute on the DSP. The only requirement on the compiler is that it should generate code
1
1.3 – The reader
that works correctly and functions as intended. There are no requirements on the performance or the size of the generated code. The compiler should also be compatible with Motorola’s C compiler and tools for the DSP56002. This makes it possible to mix generated code from the two compilers. It also means that the tools from Motorola can be used for the new compiler.
1.3 The reader It is assumed that the reader of this thesis has basic knowledge of the C programming language and some knowledge of assembly language. It is also assumed that the reader has a general knowledge of how processors work and what function a compiler has.
1.4 Reading guidelines This is a brief description of the chapters: • Chapter 1 contains an introduction and states the purpose of the thesis. • Chapter 2 describes how the DSP56002 processor works and how it can be used. • Chapter 3 contains general compiler theory that is needed to understand how a compiler works. • Chapter 4 describes the compiler LCC that was used in this thesis. • Chapter 5 describes the implementation and modifications that were done to LCC. • Chapter 6 lists the conclusions that were made and suggests further work.
2
2 DSP This chapter contains a description of how the Motorola DSP56002 processor works. This information is collected from [4].
2.1 Introduction Digital signal processing is, as the term suggests, the processing of signals by digital means. The signal is normally an electrical signal carried on a wire, but it can represent almost any kind of information and it can be processed in a wide variety of ways. Examples of digital signal processing include the following: • • • •
Filtering of signals. Convolution, which is the mixing of two signals. Correlation, which is the comparison of two signals. Rectification, amplification and transformation of a signal.
All of these tasks have earlier been performed by using analog circuits. Nowadays integrated circuits have enough processing power to perform these and many other functions. The devices performing these tasks are called digital signal processors, or DSPs. They are specialised microprocessors with architectures designed specifically for the types of operations required in digital signal processing. Like general-purpose microprocessors, the DSPs are programmable devices with its own native instruction set.
3
2.2 – Motorola DSP56002
DSPs can today be found in almost all electronic areas, such as mobile phones, personal computers, digital television decoders, surround receivers, and so on. The advantages of using a DSP instead of analog circuits are many. Generally, fewer components are needed, DSPs have higher noise immunity, it is easy to change the behaviour of a filter, filters with closer tolerances can be built, and so on. Also, since the DSP is a microcomputer, the same hardware design can be used in many different areas by simply changing the software for the DSP.
2.2 Motorola DSP56002 The Motorola DSP56002 is a general purpose DSP processor with a triple-bus Harvard architecture. This architecture can access multiple memories at the same time. It uses fixed-point arithmetic and has three function units; data arithmetic and logic unit (Data ALU), address generation unit (AGU) and program control unit (PCU). It does also have three memories, two for data (X and Y) and one for the program (P). A block diagram of the DSP56002 can bee seen in Figure 2.1.
Figure 2.1: Block diagram of the Motorola DSP56002 4
Chapter 2 – DSP
This architecture with multiple memories and buses makes it possible to, during one instruction cycle, make one computation in the data ALU while accessing the X and Y memories at the same time.
2.2.1 Data buses The data buses consists of four 24-bit wide buses called the Y data bus (y_dbus), the X data bus (x_dbus), the program data bus (p_dbus) and the global data bus (g_dbus). They are used for moving data between the function units and the memories. Data transfers between the data ALU and the X and Y memories occur over the X and Y data buses, respectively. All other data movements occur over the global data bus and instruction fetches occurs over the program data bus.
2.2.2 Address buses Addresses for the X data memory and the Y data memory are specified over the X address bus (x_abus) and the Y address bus (y_abus). Addresses for the program memory are specified over the P address bus (p_abus). All address buses are 16-bit wide.
2.2.3 Data ALU The data ALU performs all of the arithmetic and logical operations on the data. It uses a register set that consists of four 24-bit input registers, two 48-bit accumulator registers and two 8-bit accumulator extension registers. The input registers are called X0, X1, Y0 and Y1. They can also be combined into two 48-bit registers called X and Y. The two accumulators are called A and B and are 56 bits wide. Each consists of three concatenated registers, A2:A1:A0 and B2:B1:B0. The A2 and B2 are the 8-bit accumulator extension registers and they are used when more than 48-bit accuracy is needed. The input registers are used for operands to the instructions and the accumulator registers are used for both operands and the result from instructions.
2.2.4 Address generation unit The AGU performs all of the address storage and address calculations necessary to access the data in the memories. The AGU is divided into two identical halves, each of which has an address arithmetic unit that can generate one address each instruction cycle. The AGU has three sets of eight registers. They are the address registers R0 – R7, the offset 5
2.3 – Instruction set
registers N0 – N7 and the modifier registers M0 – M7. The R-registers are used for storing addresses that are used to address the memories. The N- and M-registers are used to update the R-registers in various ways. The registers are connected. So, for example, only N1 and M1 can be used to update R1.
2.2.5 Program control unit The PCU performs instruction prefetch, instruction decoding, hardware loop control and interrupt processing. It contains a 15-level system stack that is 32 bits wide and the following six registers: program counter (PC), loop address (LA), loop counter (LC), status register (SR), operating mode register (OMR) and stack pointer (SP).
2.3 Instruction set The instruction set can be seen in Appendix A on page 55. About half of the available instructions allow the use of parallel data moves.
2.4 Assembly The instruction syntax is organized in four columns; opcode, operands and two parallel move fields. An example of a typical assembly instruction can be seen here: Opcode MAC
Operands X0,Y0,A
XDB YDB X:(R0)+,X0 Y:(R4)+,Y0
The opcode column specifies the operation that should be performed. The operands column specifies which operands the opcode should use. The XDB and YDB columns specify optional data transfers over the X data bus and the Y data bus. The address space qualifiers X: and Y: indicate which memory is being referenced. This is an example of a small assembly program: var_a var_b
6
ORG dc dc
Y: 42 48
ORG MOVE MOVE
P:$40 Y:var_a,X0 Y:var_b,A
Chapter 2 – DSP
ADD MOVE
X0,A A,Y:var_a
This program simply adds the variables var_a and var_b and stores the result in var_a. This is a list of some of the features of the assembler that is used in this thesis: • Labels: If the first character on a line is not a space or a tab it is a label. Labels are used for variables and jump destinations. A colon is often used to end the label to increase readability of the assembly. • ORG: The ORG directive is used to indicate which memory the following statements belong to. It is also used for a lot of other memory related things. • OPT: The OPT directive is used to assign options to the assembler. • Variables: Variables are declared with a label and the DC directive to define a constant. • GLOBAL: The GLOBAL keyword is used to instruct the assembler that a variable is global. • Comments: Semicolon is used as a comment specifier. All characters to the right of the semicolon are ignored.
7
2.4 – Assembly
8
3 Compilers This chapter contains general compiler theory. Most of the information is collected from [1].
3.1 Introduction A compiler is a program that reads a program written in one language and translates it into an equivalent program in another language. An important part of this process is to report the presence of errors in the source program to the user. There exists thousands of different compilers for different source languages and target languages, and there also exists many different types of compilers. However, the basic principles of how the compilers work are the same. This chapter will discuss these basic principles.
3.2 The analysis-synthesis model There are two parts to compilation: analysis and synthesis. The analysis part breaks up the source program into consecutive pieces and creates an intermediate representation of the source program. The synthesis part constructs the desired target program from the intermediate representation.
9
3.3 – Phases
During analysis the operations stated in the source program are determined and recorded in a hierarchical structure called a tree. Often a special kind of tree called a syntax tree is used. In the synthesis part of the compilation the output is generated from the contents of the syntax tree. There is often also some sort of optimization of the generated source in this part.
3.3 Phases A compiler operates in phases, each of which transforms the source program from one representation to another. A typical decomposition of a compiler is shown in Figure 3.1. The following sections will discuss the different phases and how they are connected.
Figure 3.1: Phases of a compiler 10
Chapter 3 – Compilers
3.4 Analysis The analysis consists of three phases: lexical analysis, syntax analysis and semantic analysis.
3.4.1 Lexical analysis Lexical analysis, sometimes called scanning, is where the stream of characters that make up the source program is scanned left-to-right and transformed into groups of characters called tokens. For example, the characters in the statement result = start + rate * 60 would be transformed into the following tokens: 1. 2. 3. 4. 5. 6. 7.
The identifier result. The assignment symbol =. The identifier start. The plus sign. The identifier rate. The multiplication sign. The number 60.
The white space is normally eliminated during lexical analysis.
3.4.2 Syntax analysis Syntax analysis, or parsing, is where the tokens of the source program is grouped into grammatical phrases. Usually the phrases of the source program is represented by a parse tree. An example of a parse tree can be seen in Figure 3.2.
11
3.4 – Analysis
Figure 3.2: Parse tree for the statement result=start+rate*60
The phrase rate*60 will be grouped together because the rules of arithmetic expressions state that multiplication is performed before addition. Context free grammars The rules for the syntax analysis is often expressed by context free grammars. The grammar gives a precise and easy to understand specification of the syntax of the programming language. It is also possible to construct a parser from a grammar by using automated tools. For example, an if-else statement in C has the form: if ( expression ) statement else statement The statement is the concatenation of the keyword if, an opening parenthesis, an expression, a closing parenthesis, a statement, the keyword else, and another statement. Using the variable expr for expression and stmt for statement, this rule can be expressed as: stmt → if ( expr ) stmt else stmt The arrow may be read as “can have the form”. This kind of rule is called a production. In a production lexical elements like the keyword if and the parenthesis are called tokens. Variables like expr and stmt represent sequences of tokens and are called nonterminals. A context free grammar has four components: 1. A set of tokens, known as terminal symbols. 2. A set of nonterminals.
12
Chapter 3 – Compilers
3. A set of productions where each production consists of a nonterminal, an arrow, and a sequence of tokens and/or nonterminals. 4. A designation of one of the nonterminals as the start symbol. The following is an example of a simple grammar that can parse the right hand side of the assignment statement in Figure 3.2: expr → identifier expr → number expr → expr + expr | expr * expr The symbol | is used to separate multiple productions on one line and can be read as “or”. By using expr as the start symbol the derivation of the right hand side of the assignment statement could look like this: expr → expr + expr → identifier + expr → identifier + expr * expr → identifier + identifier * expr → identifier + identifier * number A grammar derives strings by beginning with the start symbol and repeatedly replacing a nonterminal by the right side of the production for that nonterminal. The set of token strings that can be derived from the start symbol form the language defined by the grammar. Syntax tree A more common internal representation of the syntactic structure is the syntax tree. It is a compressed representation of the parse tree where the operators appear as the nodes, and the operands of an operator are the children for that node. An example of a syntax tree is seen in Figure 3.3.
Figure 3.3: Syntax tree for the statement result=start+rate*60
13
3.4 – Analysis
3.4.3 Semantic analysis The semantic analysis phase checks the source program for semantic errors and gather type information for the code generation phase. It uses the hierarchical structure generated in the syntax analysis phase to identify the operators and operands of expressions and statements. This checking ensures that certain kinds of programming errors will be detected and reported. Examples of semantic checks can be: • Type checks: The compiler should report an error if an operator is applied to an incompatible operand. For example, if an integer variable is added to a function. It can also check that parameters to functions are correct in type and number. • Flow-of-control checks: Statements that causes the flow of control to leave a construct must have some place to which to transfer the flow of control. For example, a break statement in C causes the flow of control to leave the enclosing while, for or switch statement. If break is used outside of one of those an error is generated. • Uniqueness checks: Sometimes an object can only be defined once. For example, the case labels in a switch statement in C must be unique, and variables with the same name in the same scope is not permitted. • Name related checks: Sometimes the same name must appear at multiple locations. For example, in ADA a loop or block may have a name that appears at the beginning and at the end of the construct. There are many more different types of checks that can be needed to be performed depending on the language. In C for example, functions and variables must be declared before they are used, something that is not necessary in some languages. The type checking does not always have to result in an error. For example, a type mismatch can sometimes be resolved by converting the operand. If a in the statement a = a * 2; is a floating point number, the integer 2 must be converted to a floating point number before the multiplication can take place. This is accomplished by inserting a new node that explicitly converts an integer to a floating point number in the syntax tree.
14
Chapter 3 – Compilers
Since programming languages are so different and the semantic checks needed by the languages are so different there is no systematic way perform the semantic checks. It is usually done by traversing the tree and examining the nodes or during the syntax analysis phase.
3.5 Synthesis The synthesis consists of the three phases intermediate code generation, code optimizer and code generator. It is responsible for transforming the source that is now in the form of a syntax tree to the output language.
3.5.1 Intermediate code generation After the syntax and semantic analysis some compilers generate a machine independent intermediate form of the source program. Although the source program can be translated directly to the target language from the syntax tree, there are some benefits of using an intermediate form: • A machine independent optimizer can be used on the intermediate representation. • Retargeting is made easier. Creating a compiler for a different machine can be done by replacing a smaller part of the compiler than would have otherwise been necessary. The intermediate representation should have two important properties; it should be easy to generate and it should be easy to transform into the target program. A common way to solve this is to use a so called three-address code. It is very similar to an assembly language where each memory location can be used as a register. The code consists of a sequence of instructions, each of which can have at most three operands. For example, the assignment statement from Figure 3.2 might look like this: temp1 = rate * 60 temp2 = temp1 + start result = temp2 There are also statements for conditional and unconditional jumps, procedure calls, return statements, indexed assignment to be used on arrays, and address and pointer assignments. The instruction set of three-address codes must be large enough to implement the operations in the source language, but a smaller 15
3.5 – Synthesis
instruction set is easier to implement and retarget. However, if it is too small the intermediate code generator can be forced to generate long sequences of statements for some source language operations. It will then be more difficult for the optimizer and the code generator to produce good code.
3.5.2 Code optimization The code optimizer will attempt to improve the intermediate code so that faster running machine code will be generated. It can sometimes also be of interest to make the code smaller. For DSP processors code with lower power consumption is sometimes preferred. There are two types of optimizations that can be done; machine independent and machine dependent. Machine independent optimizations are typically done using the intermediate form as the base and does not consider any details of the target architecture when making optimization decisions. It is often very general in nature. Machine dependent optimizations can be done both on the intermediate form and the generated code. These optimizations consider the target architecture specifically and uses special instructions such as hardware loops and so on. There are a number of common optimization techniques. Constant propagation Constant propagation is simply the replacement of variable references with constant references when possible. For example, the statement a = 3; function_call(a + 42); becomes function_call(3 + 42); Constant folding Expressions with constant operands can be calculated at compile time. The example above would be transformed to function_call(45); Programmers usually do not write expressions such as 3+42 directly, but these expressions are quite common after macro expansion and other optimizations such as constant propagation.
16
Chapter 3 – Compilers
Common subexpression elimination A common subexpression, or CSE, is created when two or more expressions compute the same value. The expression is calculated once to a temporary variable that is used instead of the CSE. For example, the statement array1[i + 1] = array2[i + 1]; will be transformed to temp1 = i + 1; array1[temp1] = array2[temp1]; Dead code elimination Code that is never reached or that does not affect the program can be eliminated. For example, this code fragment int global; void foo(void){ int k = 1; global = 1; global = 2; } will transform into the following int global; void foo(void){ global = 2; } Expression simplification Some expressions can be simplified by replacing them with a more efficient expression. For example, i+0 will be replaced by i, i*0 and i-i by 0, and so on. Code motion Expressions in a loop that gives the same result each time the loop is iterated can be moved outside the loop and calculated only once before entering the loop.
17
3.6 – Symbol table
Strength reduction Strength reduction replaces expensive instructions with less expensive instructions. For instance, a popular strength reduction is to replace a multiplication by a constant power of two with a left shift.
3.5.3 Code generation The final phase of the compiler is the generation of target code. The target code is usually relocatable machine code or assembly code. Memory locations are selected for each of the variables used in the source program and the intermediate instructions are translated into one or more assembly level instructions that perform the same task. A vital part of code generation is the assignment of registers to variables, since that can greatly affect the performance of the generated code. Using the example from the previous sections, the generated code might look like this MOVE MUL MOV ADD MOVE
rate, R1 #60, R1 start, R2 R1, R2 R2, result
3.6 Symbol table An essential part of the compiler is to keep track of the identifiers used in the source program and to collect information about various attributes of each identifier. These attributes contains information about the name and type of the identifier, its size, scope and so on. For functions and procedures it also contains the number and types of its arguments and the return type. In a similar way it works for more complex data types like arrays and structures. The symbol table is a data structure that contains a record for each identifier and fields for the attributes of the identifier. The data structure makes it possible to search for identifiers and add or retrieve the attributes and to add new identifiers. When an identifier is found in the lexical analysis its name is added to the symbol table if its not already there. The index in the symbol table is then passed along in the token and that index is used to refer to the identifier from there on. In the later phases of compilation information
18
Chapter 3 – Compilers
about the type and other attributes are added and is used in various ways.
3.7 Error handler It is important that the compiler can detect errors and deal with them in a reasonable way. When an error is encountered the compiler emits an error message containing the location of the error in the source program and a message stating the type of error and it then tries to continue with the compilation. It can sometimes be difficult for the compiler to know what to do to continue when an error has been detected. One way is to, for example, skip all input until the next semicolon to get to the next statement. As soon as the error count is greater than zero a flag is set and the compiler will stop execution after the semantic analyzer phase. There is no point in generating the target program when there exists errors in the source program. The compiler can also detect minor errors that will not stop the compilation and emit warnings about these errors instead.
3.8 Front and back end Often the phases are collected into a front end and a back end. The front end consists of the phases that depend on source language and are largely independent of the target machine. These normally include lexical analysis, syntax analysis, semantic analysis and the generation of intermediate code. The machine independent optimizations can also be done in the front end. The creation of the symbol table and most of the error handling is also done in the front end. The back end includes the parts of the compiler that are dependant on the target machine, and these parts usually does not depend on the source language, only the intermediate code. The back end therefore consists of the code optimizer and the code generator. It also uses the symbol table and error handler. This division of the design makes it easy to take the front end of a compiler and combine it with a new back end to produce a compiler for the same source language to a different target machine. This is called retargeting.
19
3.9 – Environment
3.9 Environment In addition to the compiler, several other programs are required if an executable program is to be created. See Figure 3.4.
Figure 3.4: A compiler system
3.9.1 Preprocessor The preprocessor produce the input to the compiler. It often performs different kinds of text processing, for example macro processing and file inclusion. In C for example, all lines beginning with a # is an instruction to the preprocessor. #define FAIL -1 causes all occurrences of FAIL to be replaced by -1, and #include will include the file file.h in the source program. In C the preprocessor also removes the comments from the source program.
3.9.2 Assembler Some compilers produce assembly code, and that must be passed to an assembler for further processing. The assembler works much like a compiler and translates the assembly source to relocatable machine code. 20
Chapter 3 – Compilers
3.9.3 Linker and loader The linker makes it possible to combine several relocatable machine code files into a single program. The different machine code files can be the result from several compilations, and some may be library files. The linker resolves external references in the input files so that data and functions from the different files can be used by each other. When all the external references are resolved the loader takes the relocatable machine code and alters all relocatable addresses to real addresses and places the code and the data in its proper locations and creates the output file.
3.10 Compiler tools Since most compilers use the same structure and function in the same way, specialized tools have been developed that helps implement the various components of the compiler. These tools use specialized languages for specifying and implementing the components, and many use algorithms that are quite sophisticated. The following is a list of some compiler construction tools: • Scanner generators: These automatically generate lexical analyzers. Usually from a specification based on regular expressions. Examples include flex and lex. • Parser generators: These produce syntax analyzers from specifications that is normally based on a context free grammar. Before the parser generators appeared, the parser was the most time consuming part to implement. Now it is considered to be one of the easiest to implement thanks to the parser generators. Examples of parser generators are yacc and bison. • Syntax-directed translation engines: These produce routines that walk the parse tree and generates intermediate code. • Automatic code generators: These tools generates routines that translates the intermediate language into the machine language for the target machine by the help of a collection of rules. The basic technique is template matching. The intermediate code statements are replaced by templates that represent sequences of machine instructions.
21
3.10 – Compiler tools
22
4 LCC This chapter describes how the compiler LCC works. Most of this information is collected from [2] and [3].
4.1 Introduction LCC is a free ANSI C compiler that is designed to be retargetable. The source code is available for download from the internet [6] under a license [7] that imposes almost no restrictions at all. This compiler was chosen because it is very small and simple. It is designed in a way so that it is easy to retarget it to generate code for other processors. There is also excellent documentation of LCC in the form of a book that describes every detail of the implementation of the entire compiler. It is called “A Retargetable C Compiler: Design and Implementation” and it was used extensively during this thesis. The thesis could probably not have been completed without the book. Another compiler candidate was the GNU C Compiler, or GCC, from the GNU Compiler Collection, which is an open source C compiler. GCC would probably have generated better and faster code, but it was not chosen because it is much bigger and more complex than LCC. Also, the same kind of documentation that was available for LCC was not available for GCC.
23
4.2 – C
4.2 C C is a general purpose programming language that was developed during the 1970’s by Brian Kernighan and Dennis Ritchie and it is still widely used today. It is a relatively low level language where the basic data types in the language correspond to real data types found in the hardware. The language provides no operations to deal directly with composite data types, such as strings, arrays, lists and so on. There are no input/output facilities and no file access facilities. All these higher level operations must be provided by library functions. This, and several other limitations, has some advantages. It makes the language small and relatively easy to learn. It does also mean that compilers for the language will be smaller and easier to construct. C has become very popular and there exists compilers for many different processors and operating systems. Although it is far from an ideal language for DSP processors, it is still extensively used for them. That is probably because it is such a simple and low level language, which makes it easier to construct a compiler that generates efficient code for the DSP processors. Over the years the C programming language has evolved and been standardized a couple of times. The first version, called K&R C (from Kernighan and Ritchie), is derived from the reference manual in the first edition of the book “The C programming language” by Brian Kernighan and Dennis Ritchie. In 1989 ANSI standardized the language, and it is commonly referred to as ANSI C or C89. ISO has released two standards for C, and they are called ISO C90 and ISO C99.
4.3 The compiler The following sections will describe the different phases of the compiler and how they work.
4.3.1 Lexical analysis The lexical analyzer reads source text and produces tokens. For each token the lexical analyzer returns its token code and zero or more associated values. The token codes for single character tokens, for example = and +, are the characters themselves. For tokens that can consist of one or more characters, for example identifiers and constants, defined constants are used. For example, the expression ptr = 42 results in the following token stream 24
Chapter 4 – LCC
ID '=' ICON
"ptr"
symbol table entry for ptr
"42"
symbol table entry for 42
The token code for the operator = is the numeric value of =, and it does not have any associated values. The token code for the identifier ptr is the value of the constant ID, and the associated values are the identifier string itself and a pointer to the symbol table entry for the identifier. The integer constant 42 returns the token ICON and the associated values "42" and a pointer to the symbol table. Keywords, such as for and switch, have their own token codes to distinguish them from identifiers. The lexical analyzer also tracks the source coordinates for each token. These coordinates contains the file name, line number and position on the line of the first character of the token. The coordinates are used to locate errors when they are found. Recognizing tokens The lexical analyzer in LCC is written by hand, it is not generated by a tool. This is due to the fact that the lexical structure in C is simple and that generated analyzers tend to be large and slow. The lexical analyzer is used by calling the function gettok(), which returns the next token. The gettok() function recognizes a token by using a switch statement on the first character in the token to classify it. It then consumes the following characters that make up the token. The following is a small sample of the code ... switch (*rcp++) { ... case '1,y1 cmp y1,a jne L2 move Y:Fres,a move Y:Fx,y1 add y1,a move a,Y:Fres jmp L3 L2: move Y:Fx,y1 move y1,Y:Fres L3: L1: move Y:-(r6),y1 move Y:-(r6),ssh tst a Y:-(r6),r0 rts ;;;; Function main ends endsec end
60
entry sequence/ function prologue
body of main()
exit sequence/ function epilogue
boilerplate
C dsp56k.md This appendix contains the entire contents of the file dsp56k.md. It is used to create the back end for the Motorola DSP56002. %{ #include “c.h” #include “time.h” #define debug2(x) (void)(xflag&&((x),0)) #define #define #define #define #define static static static static static static static static static static static static static static static static static static static static static
NODEPTR_TYPE Node OP_LABEL(p) ((p)->op) LEFT_CHILD(p) ((p)->kids[0]) RIGHT_CHILD(p) ((p)->kids[1]) STATE_LABEL(p) ((p)->x.state)
void void void void void void void void void void void void void void void void void void void void void
address(Symbol, Symbol, long); blkfetch(int, int, int, int); blkloop(int, int, int, int, int, int[]); blkstore(int, int, int, int); defaddress(Symbol); defconst(int, int, Value); defstring(int, char *); defsymbol(Symbol); doarg(Node); emit2(Node); export(Symbol); clobber(Node); function(Symbol, Symbol [], Symbol [], int); global(Symbol); import(Symbol); local(Symbol); progbeg(int, char **); progend(void); segment(int); space(int); target(Node);
static int equal(Node p, int a); static static static static
Symbol Symbol Symbol Symbol
ireg[32], ireg2[32], areg[32], areg2[32]; rreg[32]; iregw, ireg2w, aregw, areg2w; rregw;
static int cseg; static int retstruct; %} %start stmt %term CNSTF1=1041 CNSTF2=2065 %term CNSTI1=1045 CNSTI2=2069 %term CNSTP1=1047
61
%term CNSTU1=1046 CNSTU2=2070 %term %term %term %term %term
ARGB=41 ARGF1=1057 ARGF2=2081 ARGI1=1061 ARGI2=2085 ARGP1=1063 ARGU1=1062 ARGU2=2086
%term %term %term %term %term
ASGNB=57 ASGNF1=1073 ASGNF2=2097 ASGNI1=1077 ASGNI2=2101 ASGNP1=1079 ASGNU1=1078 ASGNU2=2102
%term %term %term %term %term
INDIRB=73 INDIRF1=1089 INDIRF2=2113 INDIRI1=1093 INDIRI2=2117 INDIRP1=1095 INDIRU1=1094 INDIRU2=2118
%term CVFF1=1137 CVFF2=2161 %term CVFI1=1141 CVFI2=2165 %term CVIF1=1153 CVIF2=2177 %term CVII1=1157 CVII2=2181 %term CVIU1=1158 CVIU2=2182 %term CVPU1=1174 %term CVUI1=1205 CVUI2=2229 %term CVUP1=1207 %term CVUU1=1206 CVUU2=2230 %term NEGF1=1217 NEGF2=2241 %term NEGI1=1221 NEGI2=2245 %term %term %term %term %term %term
CALLB=217 CALLF1=1233 CALLF2=2257 CALLI1=1237 CALLI2=2261 CALLP1=1239 CALLU1=1238 CALLU2=2262 CALLV=216
%term %term %term %term %term
RETF1=1265 RETF2=2289 RETI1=1269 RETI2=2293 RETP1=1271 RETU1=1270 RETU2=2294 RETV=248
%term ADDRGP1=1287 %term ADDRFP1=1303 %term ADDRLP1=1319 %term %term %term %term
ADDF1=1329 ADDF2=2353 ADDI1=1333 ADDI2=2357 ADDP1=1335 ADDU1=1334 ADDU2=2358
%term %term %term %term
SUBF1=1345 SUBF2=2369 SUBI1=1349 SUBI2=2373 SUBP1=1351 SUBU1=1350 SUBU2=2374
%term LSHI1=1365 LSHI2=2389 %term LSHU1=1366 LSHU2=2390 %term MODI1=1381 MODI2=2405 %term MODU1=1382 MODU2=2406 %term RSHI1=1397 RSHI2=2421 %term RSHU1=1398 RSHU2=2422 %term BANDI1=1413 BANDI2=2437 %term BANDU1=1414 BANDU2=2438 %term BCOMI1=1429 BCOMI2=2453 %term BCOMU1=1430 BCOMU2=2454 %term BORI1=1445 BORI2=2469 %term BORU1=1446 BORU2=2470 %term BXORI1=1461 BXORI2=2485 %term BXORU1=1462 BXORU2=2486 %term DIVF1=1473 DIVF2=2497 %term DIVI1=1477 DIVI2=2501 %term DIVU1=1478 DIVU2=2502 %term MULF1=1489 MULF2=2513 %term MULI1=1493 MULI2=2517 %term MULU1=1494 MULU2=2518
62
Appendix C – dsp56k.md %term EQF1=1505 EQF2=2529 %term EQI1=1509 EQI2=2533 %term EQU1=1510 EQU2=2534 %term GEF1=1521 GEF2=2545 %term GEI1=1525 GEI2=2549 %term GEU1=1526 GEU2=2550 %term GTF1=1537 GTF2=2561 %term GTI1=1541 GTI2=2565 %term GTU1=1542 GTU2=2566 %term LEF1=1553 LEF2=2577 %term LEI1=1557 LEI2=2581 %term LEU1=1558 LEU2=2582 %term LTF1=1569 LTF2=2593 %term LTI1=1573 LTI2=2597 %term LTU1=1574 LTU2=2598 %term NEF1=1585 NEF2=2609 %term NEI1=1589 NEI2=2613 %term NEU1=1590 NEU2=2614 %term JUMPV=584 %term LABELV=600 %term VREGP=711 %term %term %term %term %term %term %term %term
LOADI1=1253 LOADI2=2277 LOADU1=1254 LOADU2=2278 LOADB=233 LOADF1=1249 LOADF2=2273 LOADP1=1255
%% reg: INDIRI1(VREGP) reg: INDIRI2(VREGP) reg: INDIRU1(VREGP) reg: INDIRU2(VREGP) reg: INDIRF1(VREGP) reg: INDIRF2(VREGP) rreg: INDIRP1(VREGP) stmt: stmt: stmt: stmt: stmt: stmt: stmt: con: reg: con: reg: con: reg: reg:
ASGNI1(VREGP, ASGNI2(VREGP, ASGNU1(VREGP, ASGNU2(VREGP, ASGNF1(VREGP, ASGNF2(VREGP, ASGNP1(VREGP,
“# “# “# “# “# “# “#
read read read read read read read
reg) reg) reg) reg) reg) reg) rreg)
CNSTI1 CNSTI2 CNSTU1 CNSTU2 CNSTP1 CNSTF1 CNSTF2
“# “# “# “# “# “# “#
register\n” register\n” register\n” register\n” register\n” register\n” register\n”
write write write write write write write
“%a” “# \tmove “%a” “# \tmove “%a” “# \tmove “# \tmove
register\n” register\n” register\n” register\n” register\n” register\n” register\n”
\t#>%a,%c\n” 2+2 \t#>%a,%c\n” 2+2 \t#>%a,%c\n” 2 \t#>%a,%c\n” 2+2
reg: LOADI1(reg) reg: LOADU1(reg) reg: LOADU1(rreg) reg: LOADI2(reg) reg: LOADU2(reg) reg: LOADF1(reg) reg: LOADF2(reg) rreg: LOADP1(rreg) rreg: LOADP1(reg)
“\tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2
reg: con rreg: con
“\tmove \t#>%0,%c\n” 2 “\tmove \t#%0,%c\n” 2
rreg: ADDRGP1 rreg: ADDRFP1 rreg: ADDRLP1
“\tmove \t#%a,%c\n” 2 “\tmove \t#%a,n0\n\tlua \t(r0)+n0,%c\n” 2+4 “\tmove \t#%a,n6\n\tlua \t(r6)+n6,%c\n” 2+4
stmt: reg stmt: LABELV
““ “%a:\n”
addr: rreg addr: ADDRGP1
“Y:(%0)” “Y:%a”
stmt: stmt: stmt: stmt:
ASGNI1(addr, ASGNI2(addr, ASGNU1(addr, ASGNU2(addr,
reg) reg) reg) reg)
“\tmove \t%1,%0\n” 2 “# \tmove \t%1,%0\n” 2 “\tmove \t%1,%0\n” 2 “# \tmove \t%1,%0\n” 2
63
stmt: stmt: stmt: stmt:
ASGNF1(addr, reg) ASGNF2(addr, reg) ASGNP1(addr, rreg) ASGNB(rreg, INDIRB(rreg))
“\tmove \t%1,%0\n” 2 “# \tmove \t%1,%0\n” 2 “\tmove \t%1,%0\n” 2 “# ASGNB\n”
reg: INDIRI1(addr) reg: INDIRI2(addr) reg: INDIRU1(addr) reg: INDIRU2(addr) reg: INDIRF1(addr) reg: INDIRF2(addr) rreg: INDIRP1(addr)
“\tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2 “# \tmove \t%0,%c\n” 2 “\tmove \t%0,%c\n” 2
reg: reg: reg: reg: reg: reg:
ADDI1(reg, ADDI2(reg, ADDU1(reg, ADDU2(reg, ADDF1(reg, ADDF2(reg,
reg) reg) reg) reg) reg) reg)
“\tadd “\tadd “\tadd “\tadd “\tadd “\tadd
\t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n”
2 2 2 2 2 2
reg: reg: reg: reg: reg: reg:
SUBI1(reg, SUBI2(reg, SUBU1(reg, SUBU2(reg, SUBF1(reg, SUBF2(reg,
reg) reg) reg) reg) reg) reg)
“\tsub “\tsub “\tsub “\tsub “\tsub “\tsub
\t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n” \t%1,%0\n”
2 2 2 2 2 2
rc: reg rc: con rreg: ADDP1(rc, rreg) rreg: ADDP1(rreg, rc)
“%0” “%#%0” “# \tmove \t%0,n8\n\tlua \t(%1)+n8,%c\n” 2+4 “# \tmove \t%1,n8\n\tlua \t(%0)+n8,%c\n” 2+4
rreg: SUBP1(rc, rreg) rreg: SUBP1(rreg, rc)
“# \tmove \t%0,n8\n\tlua \t(%1)-n8,%c\n” 2+4 “# \tmove \t%1,n8\n\tlua \t(%0)-n8,%c\n” 2+4
reg: reg: reg: reg: reg: reg:
MULI1(reg, MULI2(reg, MULU1(reg, MULU2(reg, MULF1(reg, MULF2(reg,
reg) reg) reg) reg) reg) reg)
“# “# “# “# “# “#
\tmpy \tmpy \tmpy \tmpy \tmpy \tmpy
\t%0,%1,%c\n\tasr \t%c\n\tmove \t%c0,%c\n” 2+2+2 \t%0,%1,%c\n” 2 \t%0,%1,%c\n\tasr \t%c\n\tmove \t%c0,%c\n” 2+2+2 \t%0,%1,%c\n” 2 \t%0,%1,%c\n\tmove \t%c0,%c\n” 2+2 \t%0,%1,%c\n” 2
reg: reg: reg: reg: reg: reg:
DIVI1(reg, DIVI2(reg, DIVU1(reg, DIVU2(reg, DIVF1(reg, DIVF2(reg,
reg) reg) reg) reg) reg) reg)
“# “# “# “# “# “#
\tdiv \tdiv \tdiv \tdiv \tdiv \tdiv
\t%0,%1\n” \t%0,%1\n” \t%0,%1\n” \t%0,%1\n” \t%0,%1\n” \t%0,%1\n”
2 2 2 2 2 2
reg: reg: reg: reg:
MODI1(reg, MODI2(reg, MODU1(reg, MODU2(reg,
reg) reg) reg) reg)
“# “# “# “#
\tmod \tmod \tmod \tmod
\t%0,%1\n” \t%0,%1\n” \t%0,%1\n” \t%0,%1\n”
2 2 2 2
reg: reg: reg: reg: reg: reg:
CVII1(reg) CVII2(reg) CVIU1(reg) CVIU2(reg) CVIF1(reg) CVIF2(reg)
“# “# “# “# “# “#
\tmove \tmove \tmove \tmove \tmove \tmove
\t%0,%c\n” \t%0,%c\n” \t%0,%c\n” \t%0,%c\n” \t%0,%c\n” \t%0,%c\n”
move(a) move(a) move(a) move(a) move(a) move(a)
reg: CVUI1(reg) reg: CVUI2(reg) reg: CVUU1(reg) reg: CVUU2(reg) rreg: CVUP1(reg)
“# \tmove \t%0,%c\n” move(a) “# \tmove \t%0,%c\n” move(a) “# \tmove \t%0,%c\n” move(a) “# \tmove \t%0,%c\n” move(a) “\tmove \t%0,%c\n” move(a)
reg: CVPU1(rreg)
“\tmove \t%0,%c\n” move(a)
reg: reg: reg: reg:
“# “# “# “#
CVFF1(reg) CVFF2(reg) CVFI1(reg) CVFI2(reg)
stmt: JUMPV(jaddr)
\tmove \tmove \tmove \tmove
\t%0,%c\n” \t%0,%c\n” \t%0,%c\n” \t%0,%c\n”
move(a) move(a) move(a) move(a)
“\tjmp \t%0\n” 4
reg: CALLB(jaddr, rreg)
“\tmove \t%1,%c\n\tjsr \t%0\n” 2+4
reg: CALLI1(jaddr) reg: CALLI1(jaddr) reg: CALLI1(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
reg: CALLI2(jaddr) reg: CALLI2(jaddr) reg: CALLI2(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
reg: CALLP1(jaddr) reg: CALLP1(jaddr) reg: CALLP1(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
reg: CALLU1(jaddr) reg: CALLU1(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1)
64
Appendix C – dsp56k.md reg: CALLU1(jaddr)
“\tjsr \t%0\n” equal(a, 0)
reg: CALLU2(jaddr) reg: CALLU2(jaddr) reg: CALLU2(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
reg: CALLF1(jaddr) reg: CALLF1(jaddr) reg: CALLF1(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
reg: CALLF2(jaddr) reg: CALLF2(jaddr) reg: CALLF2(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
stmt: CALLV(jaddr) stmt: CALLV(jaddr) stmt: CALLV(jaddr)
“\tjsr \t%0\n\tmove \t#%a,n6\n\tmove \t(r6)-n6\n” 4+2 “\tjsr \t%0\n\tmove \t(r6)-\n” equal(a, 1) “\tjsr \t%0\n” equal(a, 0)
jaddr: ADDRGP1 jaddr: CNSTP1 jaddr: rreg
“%a” “%a” “(%0)”
stmt: stmt: stmt: stmt: stmt: stmt: stmt: stmt:
ARGI1(reg) “\tmove \t%0,Y:(r6)+\n” 2 ARGI2(reg) “# \tmove \t%0,Y:(r6)+\n” 2 ARGU1(reg) “\tmove \t%0,Y:(r6)+\n” 2 ARGU2(reg) “# \tmove \t%0,Y:(r6)+\n” 2 ARGF1(reg) “\tmove \t%0,Y:(r6)+\n” 2 ARGF2(reg) “# \tmove \t%0,Y:(r6)+\n” 2 ARGP1(rreg) “\tmove \t%0,Y:(r6)+\n” 2 ARGB(INDIRB(rreg)) “# ARGB\n” 8
stmt: stmt: stmt: stmt: stmt: stmt: stmt:
RETI1(reg) RETI2(reg) RETU1(reg) RETU2(reg) RETF1(reg) RETF2(reg) RETP1(rreg)
“# “# “# “# “# “# “#
stmt: stmt: stmt: stmt: stmt: stmt:
EQI1(reg, EQI2(reg, EQU1(reg, EQU2(reg, EQF1(reg, EQF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjeq \t%1,%0\n\tjeq \t%1,%0\n\tjeq \t%1,%0\n\tjeq \t%1,%0\n\tjeq \t%1,%0\n\tjeq
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
stmt: stmt: stmt: stmt: stmt: stmt:
GEI1(reg, GEI2(reg, GEU1(reg, GEU2(reg, GEF1(reg, GEF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjge \t%1,%0\n\tjge \t%1,%0\n\tjge \t%1,%0\n\tjge \t%1,%0\n\tjge \t%1,%0\n\tjge
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
stmt: stmt: stmt: stmt: stmt: stmt:
GTI1(reg, GTI2(reg, GTU1(reg, GTU2(reg, GTF1(reg, GTF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjgt \t%1,%0\n\tjgt \t%1,%0\n\tjgt \t%1,%0\n\tjgt \t%1,%0\n\tjgt \t%1,%0\n\tjgt
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
stmt: stmt: stmt: stmt: stmt: stmt:
LEI1(reg, LEI2(reg, LEU1(reg, LEU2(reg, LEF1(reg, LEF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjle \t%1,%0\n\tjle \t%1,%0\n\tjle \t%1,%0\n\tjle \t%1,%0\n\tjle \t%1,%0\n\tjle
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
stmt: stmt: stmt: stmt: stmt: stmt:
LTI1(reg, LTI2(reg, LTU1(reg, LTU2(reg, LTF1(reg, LTF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjlt \t%1,%0\n\tjlt \t%1,%0\n\tjlt \t%1,%0\n\tjlt \t%1,%0\n\tjlt \t%1,%0\n\tjlt
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
stmt: stmt: stmt: stmt: stmt: stmt:
NEI1(reg, NEI2(reg, NEU1(reg, NEU2(reg, NEF1(reg, NEF2(reg,
reg) reg) reg) reg) reg) reg)
“\tcmp “\tcmp “\tcmp “\tcmp “\tcmp “\tcmp
\t%1,%0\n\tjne \t%1,%0\n\tjne \t%1,%0\n\tjne \t%1,%0\n\tjne \t%1,%0\n\tjne \t%1,%0\n\tjne
\t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n” \t%a\n”
2+4 2+4 2+4 2+4 2+4 2+4
reg: reg: reg: reg:
NEGI1(reg) NEGI2(reg) NEGF1(reg) NEGF2(reg)
“\tneg “\tneg “\tneg “\tneg
\t%0\n” \t%0\n” \t%0\n” \t%0\n”
con1: CNSTI1 reg: reg: reg: reg:
LSHI1(reg, LSHI1(reg, LSHI2(reg, LSHI2(reg,
rts\n” rts\n” rts\n” rts\n” rts\n” rts\n” rts\n”
4 4 4 4 4 4 4
2 2 2 2
“%a” range(a,1,1) rc) con1) rc) con1)
“\trep “\tasl “\trep “\tasl
\t%1\n\tasl \t%0\n” 4+2 \t%0\n” 4 \t%1\n\tasl \t%0\n” 4+2 \t%0\n” 4
65
reg: reg: reg: reg:
LSHU1(reg, LSHU1(reg, LSHU2(reg, LSHU2(reg,
rc) con1) rc) con1)
“\trep “\tlsl “\trep “\tasl
reg: reg: reg: reg: reg: reg: reg: reg:
RSHI1(reg, RSHI1(reg, RSHI2(reg, RSHI2(reg, RSHU1(reg, RSHU1(reg, RSHU2(reg, RSHU2(reg,
rc) con1) rc) con1) rc) con1) rc) con1)
“\trep \t%1\n\tasr \t%0\n” 4+2 “\tasr \t%0\n” 4 “\trep \t%1\n\tasr \t%0\n” 4+2 “\tasr \t%0\n” 4 “\trep \t%1\n\tlsr \t%0\n” 4+2 “\tlsr \t%0\n” 4 “\tmove \t#0,%02\n\trep \t%1\n\tasr \t%0\n” 4+2 “\tmove \t#0,%02\n\tasr \t%0\n” 4
reg: reg: reg: reg:
BANDI1(reg, BANDI2(reg, BANDU1(reg, BANDU2(reg,
reg) reg) reg) reg)
reg: reg: reg: reg:
BORI1(reg, BORI2(reg, BORU1(reg, BORU2(reg,
reg: reg: reg: reg:
BXORI1(reg, BXORI2(reg, BXORU1(reg, BXORU2(reg,
reg: reg: reg: reg:
BCOMI1(reg) BCOMI2(reg) BCOMU1(reg) BCOMU2(reg)
reg) reg) reg) reg) reg) reg) reg) reg)
\t%1\n\tlsl \t%0\n” 4+2 \t%0\n” 4 \t%1\n\tasl \t%0\n” 4+2 \t%0\n” 4
“\tand \t%0,%1\n” 2 “# \tand \t%0,%1\n” 2 “\tand \t%0,%1\n” 2 “# \tand \t%0,%1\n” 2 “\tor \t%0,%1\n” 2 “# \tor \t%0,%1\n” 2 “\tor \t%0,%1\n” 2 “# \tor \t%0,%1\n” 2 “\teor \t%0,%1\n” 2 “# \teor \t%0,%1\n” 2 “\teor \t%0,%1\n” 2 “# \teor \t%0,%1\n” 2 “\tnot \t%0\n” 2 “# \tnot \t%0\n” 2 “\tnot \t%0\n” 2 “# \tnot \t%0\n” 2
%% static int equal(Node p, int a){ if(p->syms[0]->u.c.v.i == a){ return 0; } else { return LBURG_MAX; } } static void progbeg(int argc, char **argv) { int i, n; char section[256]; time_t res = time(NULL); print(“;;;; LCC-DSP56k - compiled on %s\n”, asctime(localtime(&res))); parseflags(argc, argv); ireg[0] ireg[1] ireg[2] ireg[3]
= = = =
mkreg(“x0”, mkreg(“x1”, mkreg(“y0”, mkreg(“y1”,
0, 1, 2, 3,
1, 1, 1, 1,
IREG); IREG); IREG); IREG);
ireg2[0] = mkreg(“x”, 0, 1, ireg2[1] = mkreg(“y”, 2, 1, ireg2[0]->x.regnode->mask = ireg2[1]->x.regnode->mask =
IREG); IREG); 3; // 00000011 12; // 00001100
areg[0] areg[1] areg[2] areg[3]
IREG); IREG); IREG); IREG);
= = = =
mkreg(“a0”, mkreg(“a1”, mkreg(“b0”, mkreg(“b1”,
4, 5, 6, 7,
1, 1, 1, 1,
areg2[0] = mkreg(“a”, 4, 1, areg2[1] = mkreg(“b”, 6, 1, areg2[0]->x.regnode->mask = areg2[1]->x.regnode->mask =
IREG); IREG); 48; // 00110000 192; // 11000000
for(i = 0; i < 8; i++) { rreg[i] = mkreg(stringf(“r%d”, i), i+8, 1, IREG); } iregw = mkwildcard(ireg); ireg2w = mkwildcard(ireg2); aregw = mkwildcard(areg); areg2w = mkwildcard(areg2); rregw = mkwildcard(rreg); tmask[IREG] tmask[FREG] vmask[IREG] vmask[FREG]
= = = =
0x0000BEFF; // R7,5-1 Y,X Y1,Y0,X1,X0 0x00000000; 0x00000000; 0x00000000;
// convert . to _ and remove the path in filename // (../path/to/file.c -> file_c) if(firstfile) { for(i = n = 0; firstfile[i] && i < 255; i++){
66
Appendix C – dsp56k.md char ch = firstfile[i] == ‘.’ ? ‘_’ : firstfile[i]; if(ch == ‘/’) { n = 0; } else { section[n++] = ch; } } section[i]=0; print(“\tsection %s\n”, section); } // so - write symbols // nomd - do not write macro defs // rp - generate nop insn to accomodate pipeline delay print(“\topt \tso,nomd,rp\n”); } static void progend(void) { print(“\n\tendsec\n”); print(“\tend\n”); } static Symbol rmap(int opk) { debug2(fprint(stderr, “Inside %s: %d\n”, “rmap”, opk)); switch(optype(opk)) { case P: case B: return rregw; break; case I: case U: case F: if(opsize(opk) == 1) { return iregw; } else { return ireg2w; } break; default: return 0; } } static void segment(int n) { debug2(fprint(stderr, “Inside %s: %d\n”, “segment”, n)); if(n == cseg) { return; } cseg = n; if(n == CODE){ print(“\torg p:\n”); } else { print(“\torg y:\n”); } } static void target(Node p) { debug2(fprint(stderr, “Inside %s: %x\n”, “target”, p)); switch(specific(p->op)) { case CALL+B: setreg(p, areg2[0]); //a //rtarget(p, 1, areg2[0]); //a break; case ADD+I: case SUB+I: case ADD+U: case SUB+U: case ADD+F: case SUB+F: setreg(p, areg2[0]); //a rtarget(p, 0, areg2[0]); //a break; case MUL+I: case MUL+U: case MUL+F: if(opsize(p->op) == 1) { rtarget(p, 0, ireg[2]); //y0 This is bad but prevents this rtarget(p, 1, ireg[3]); //y1 error: a*b*c -> mpy y0,y1,b setreg(p, areg2[1]); //b mpy b,x1,b } else { rtarget(p, 0, areg2[0]); //a rtarget(p, 1, areg2[1]); //b setreg(p, areg2[1]); //b } break; case DIV+I: case DIV+U: case DIV+F: if(opsize(p->op) == 1) { rtarget(p, 0, areg2[0]); //a rtarget(p, 1, ireg[0]); //x0 setreg(p, areg2[0]); //a } else { rtarget(p, 0, areg2[0]); //a rtarget(p, 1, areg2[1]); //b setreg(p, areg2[0]); //a } break; case MOD+I: case MOD+U: if(opsize(p->op) == 1) { rtarget(p, 0, areg2[0]); //a rtarget(p, 1, ireg[0]); //x0
67
setreg(p, areg2[0]); //a } else { rtarget(p, 0, areg2[0]); //a rtarget(p, 1, areg2[1]); //b setreg(p, areg2[0]); //a } break; case LSH+I: case RSH+I: case LSH+U: case RSH+U: if(opsize(p->op) == 1) { rtarget(p, 0, areg2[0]); //a setreg(p, areg[1]); //a1 } else { rtarget(p, 0, areg2[0]); //a setreg(p, areg2[0]); //a1 } break; case NEG+I: case NEG+F: rtarget(p, 0, areg2[0]); //a setreg(p, areg2[0]); //a break; case BAND+I: case BOR+I: case BAND+U: case BOR+U: case BXOR+I: case BXOR+U: if(opsize(p->op) == 1) { rtarget(p, 0, ireg[3]); //y1 rtarget(p, 1, areg[1]); //a1 setreg(p, areg[1]); //a1 } else { rtarget(p, 1, areg2[0]); //a setreg(p, areg2[0]); //a } break; case BCOM+I: case BCOM+U: if(opsize(p->op) == 1) { rtarget(p, 0, areg[1]); //a1 setreg(p, areg[1]); //a1 } else { rtarget(p, 0, areg2[0]); //a setreg(p, areg2[0]); //a } break; case EQ+I: case GE+I: case GT+I: case LE+I: case LT+I: case NE+I: case EQ+U: case GE+U: case GT+U: case LE+U: case LT+U: case NE+U: case EQ+F: case GE+F: case GT+F: case LE+F: case LT+F: case NE+F: rtarget(p, 0, areg2[0]); //a if(opsize(p->op) == 2) { rtarget(p, 1, areg2[1]); //b } break; case CALL+I: case CALL+U: case CALL+F: case CALL+P: case CALL+V: setreg(p, areg2[0]); //a break; case RET+I: case RET+U: case RET+F: rtarget(p, 0, areg2[0]); //a break; case RET+P: rtarget(p, 0, areg[1]); //a1 break; case CVI+I: case CVI+U: case CVI+F: case CVU+I: case CVU+U: case CVF+I: case CVF+F: if(opsize(p->op) == 1){ setreg(p, areg2[0]); //a } else { setreg(p, areg2[0]); //a } break; case LOAD+I: case LOAD+U: case LOAD+F: if(p->kids[0]->x.inst == _rreg_NT){ break; } if(opsize(p->op) == 1){ rtarget(p, 0, areg2[0]); //a } else { rtarget(p, 0, areg2[0]); //a } break; default: break; } } static void clobber(Node p) { debug2(fprint(stderr, “Inside %s: %x\n”, “clobber”, p)); assert(p); } static char *reg_name(int reg){ if(reg >= 0 && reg x.name; } else if(reg == 4 || reg == 6){ return areg2[reg/2 - 2]->x.name; } else if(reg == 5 || reg == 7){ return areg[reg - 4]->x.name;
68
Appendix C – dsp56k.md } else { assert(0); } } static void emit2(Node p) { debug2(fprint(stderr, “Inside %s: %x\n”, “emit2”, p)); switch(specific(p->op)) { case CNST+F: assert(0); break; case CNST+I: case CNST+U: { assert(opsize(p->op) == 2); //reg: CNSTI2 “# \tmove \t%a,%c\n” 0 int reg = getregnum(p); print(“\tmove \t#>%d,%s\n”, (int)(p->syms[0]->u.c.v.i & 0xFFFFFF), reg_name(reg)); print(“\tmove \t#>%d,%s\n”, (int)(p->syms[0]->u.c.v.i >> 24), reg_name(reg + 1)); break; } case INDIR+I: case INDIR+U: case INDIR+F: { if(p->kids[0]->op != VREG+P){ if(opsize(p->op) == 2){ int dst = getregnum(p); //reg: INDIRI2(addr) “\tmove \t%0,%c\n” print(“\tmove \t”); emitasm(p->kids[0], _addr_NT); if(p->kids[0]->x.inst == _rreg_NT){ print(“+,%s\n”, reg_name(dst)); } else { print(“,%s\n”, reg_name(dst)); } print(“\tmove \t”); emitasm(p->kids[0], _addr_NT); if(p->kids[0]->x.inst == _rreg_NT){ print(“-,%s\n”, reg_name(dst + 1)); } else { print(“+1,%s\n”, reg_name(dst + 1)); } } } break; } case ASGN+I: case ASGN+U: case ASGN+F: { if(p->kids[0]->op != VREG+P){ if(opsize(p->op) == 2){ int src = getregnum(p->kids[1]); //stmt: ASGNI2(addr, reg) “\tmove \t%1,%0\n” print(“\tmove \t%s,”, reg_name(src)); emitasm(p->kids[0], _addr_NT); if(p->kids[0]->x.inst == _rreg_NT){ print(“+\n”); } else { print(“\n”); } print(“\tmove \t%s,”, reg_name(src+1)); emitasm(p->kids[0], _addr_NT); if(p->kids[0]->x.inst == _rreg_NT){ print(“-\n”); } else { print(“+1\n”); } } } break; } case ASGN+B: { int src = getregnum(p->x.kids[1]) - 8; int dst = getregnum(p->x.kids[0]) - 8; int lab = genlabel(1); print(“\tmove \tr%d,n%d x1,Y:(r6)\n”, src, src); print(“\tmove \tr%d,n%d\n”, dst, dst); print(“\tdo \t#%d,L%d\n”, (int)p->syms[0]->u.c.v.i, lab); print(“\tmove \tY:(r%d)+,x0\n”, src); print(“\tmove \tx0,Y:(r%d)+\n”, dst); print(“L%d:\n”, lab); print(“\tmove \tn%d,r%d\n”, src, src); print(“\tmove \tn%d,r%d Y:(r6),x1\n”, dst, dst); break; } case ADD+P: //rreg: ADDP1(rreg, rc) “# \tmove \t%1,n8\n\tlua \t(%0)+n8,%c\n” //rreg: ADDP1(rc, rreg) “# \tmove \t%0,n8\n\tlua \t(%1)+n8,%c\n” if(p->kids[0]->x.inst == _rreg_NT){ int reg = getregnum(p->x.kids[0]) - 8; int dst = getregnum(p) - 8; print(“\tmove \t”); emitasm(p->kids[1], _rc_NT); print(“,n%d\n\tlua \t(r%d)+n%d,r%d\n”, reg, reg, reg, dst);
69
} else if(p->kids[1]->x.inst == _rreg_NT){ int reg = getregnum(p->x.kids[1]) - 8; int dst = getregnum(p) - 8; print(“\tmove \t”); emitasm(p->kids[0], _rc_NT); print(“,n%d\n\tlua \t(r%d)+n%d,r%d\n”, reg, reg, reg, dst); } else assert(0); break; case SUB+P: //rreg: SUBP1(rc, rreg) “# \tmove \t%0,n8\n\tlua \t(%1)-n8,%c\n” //rreg: SUBP1(rreg, rc) “# \tmove \t%1,n8\n\tlua \t(%0)-n8,%c\n” if(p->kids[0]->x.inst == _rreg_NT){ int reg = getregnum(p->x.kids[0]) - 8; int dst = getregnum(p) - 8; print(“\tmove \t”); emitasm(p->kids[1], _rc_NT); print(“,n%d\n\tlua \t(r%d)-n%d,r%d\n”, reg, reg, reg, dst); } else if(p->kids[1]->x.inst == _rreg_NT){ int reg = getregnum(p->x.kids[1]) - 8; int dst = getregnum(p) - 8; print(“\tmove \t”); emitasm(p->kids[0], _rc_NT); print(“,n%d\n\tlua \t(r%d)-n%d,r%d\n”, reg, reg, reg, dst); } else assert(0); break; case MUL+I: case MUL+U: case MUL+F: { //reg: MULI1(reg, reg) “\tmpy \t%0,%1,%c\n\tasr \t%c\n // \tmove \t%c0,%c\n” if(opsize(p->op) == 1) { int s1 = getregnum(p->x.kids[0]); int s2 = getregnum(p->x.kids[1]); int d = getregnum(p); char *src1 = p->x.kids[0]->syms[RX]->x.name; char *src2 = p->x.kids[1]->syms[RX]->x.name; if(s1 == s2){ //temp_reg = x1, x0 if x1 is taken char *temp_reg = (s1 == 0) ? (ireg[1]->x.name) : (ireg[0]->x.name); print(“\tmove \t%s,Y:(r6)\n”, temp_reg); print(“\tmove \t%s,%s\n”, src2, temp_reg); print(“\tmpy \t%s,%s,%s\n”,src1, temp_reg, p->syms[RX]->x.name); if(optype(p->op) == I || optype(p->op) == U){ print(“\tasr \t%s\n”, p->syms[RX]->x.name); print(“\tmove \t%s0,%s Y:(r6),%s\n”, p->syms[RX]->x.name, p->syms[RX]->x.name, temp_reg); } } else { print(“\tmpy \t%s,%s,%s\n”,src1, src2, p->syms[RX]->x.name); if(optype(p->op) == I || optype(p->op) == U){ print(“\tasr \t%s\n”, p->syms[RX]->x.name); print(“\tmove \t%s0,%s\n”, p->syms[RX]->x.name, p->syms[RX]->x.name); } } } else { // long mpy - copied from motorola // a and b is input regs and b is output reg int lab1 = genlabel(1); int lab2 = genlabel(1); if(optype(p->op) == F){ warning(“double multiply is not implemented; using long multiply instead\n”); } print(“\t;; begin long multiply\n”); print(“\tmoven0,y:(r6)+\n”); print(“\tmovea0,y:(r6)+\n”); print(“\tmovea1,y:(r6)+\n”); print(“\tmovex0,y:(r6)+\n”); print(“\tmovex1,y:(r6)+\n”); print(“\tmovey0,y:(r6)+\n”); print(“\tmove#$0,n0\n”); print(“\tmovea1,x0\n”); print(“\teorx0,bb1,x1\n”); print(“\tjplL%d\n”, lab1); print(“\tmove#$1,n0\n”); print(“L%d:\n”, lab1); print(“\tabsa x1,b1\n”); print(“\tabsb y1,y:(r6)+\n”); print(“\tmoveb0,y:(r6)+\n”); print(“\tmoveb1,y:(r6)\n”); print(“\tclrb b1,x0\n”); print(“\tmovex0,b0\n”); print(“\taslb\n”); print(“\taslb\n”); print(“\tclrb b1,x0\n”); print(“\tmovea1,b0\n”); print(“\taslb\n”); print(“\taslb #$7fffff,y1\n”); print(“\tmoveb1,x1\n”); print(“\tmovey:(r6)-,b\n”); print(“\tmovey:(r6),b0\n”); print(“\tmovex0,y:(r6)+\n”); print(“\tmovex1,y:(r6)\n”);
70
Appendix C – dsp56k.md print(“\taslb\n”); print(“\tandy1,b\n”); print(“\tmoveb1,x0\n”); print(“\tasrb\n”); print(“\tmoveb0,b\n”); print(“\tandy1,b\n”); print(“\tmoveb1,x1\n”); print(“\tasla\n”); print(“\tandy1,ay1,b1\n”); print(“\tmovea1,y0\n”); print(“\tasra\n”); print(“\tmovea0,y1\n”); print(“\tandy1,b\n”); print(“\tmoveb1,y1\n”); print(“\tmpyx1,y1,b\n”); print(“\tmpyx1,y0,a\n”); print(“\tmacx0,y1,a\n”); print(“\tmovea1,a2\n”); print(“\tmovea0,a1\n”); print(“\tmove#$0,a0\n”); print(“\tasra\n”); print(“\tadda,b\n”); print(“\tmpyx0,y0,a\n”); print(“\tmovey:(r6)-,y0\n”); print(“\tmovey:(r6)-,x0\n”); print(“\tmacy0,x1,a\n”); print(“\tmacx0,y1,a\n”); print(“\tclra a0,x0\n”); print(“\tmovex0,a2\n”); print(“\tasra y:(r6)-,y1\n”); print(“\tasra y:(r6)-,y0\n”); print(“\tadda,by:(r6)-,x1\n”); print(“\tasrb y:(r6)-,x0\n”); print(“\tmoven0,a\n”); print(“\ttsta y:(r6)-,a\n”); print(“\tjeqL%d\n”, lab2); print(“\tnegb\n”); print(“L%d:\n”, lab2); print(“\ttstb y:(r6)-,a0\n”); print(“\tmovey:(r6),n0\n”); print(“\t;; end long multiply\n”); } break; } case DIV+I: case DIV+U: case DIV+F:{ if(opsize(p->op) == 1){ char *src = ireg[getregnum(p->x.kids[1])]->x.name; char *dst = p->syms[RX]->x.name; int lab = genlabel(1); print(“\tmove \tb,Y:(r6)+\n”); print(“\tabs \t%s %s,b\n”, dst, dst); //b if(optype(p->op) == I || optype(p->op) == U){ print(“\tclr \t%s %s1,Y:(r6)\n”, dst, dst); print(“\tmove \tY:(r6),%s0\n”, dst); print(“\tasl \t%s\n”, dst); } print(“\trep \t#24\n”); print(“\tdiv \t%s,%s\n”, src, dst); print(“\teor \t%s,b\n”, src); //b print(“\tjpl \tL%d\n”, lab); print(“\tneg \t%s\n”, dst); print(“L%d:\tmove \t%s0,%s\n”, lab, dst, dst); print(“\tmove \tY:-(r6),b\n”); } else { // long div - copied from motorola // a and b is input regs and a is output reg int lab[11]; int i; for(i = 0; i < 11; i++){ lab[i] = genlabel(1); } if(optype(p->op) == F){ warning(“double division is not implemented; using long division instead\n”); } print(“\t;;-- begin long division\n”); print(“move n0,y:(r6)+\n”); print(“move b0,y:(r6)+\n”); print(“move b1,y:(r6)+\n”); print(“move x0,y:(r6)+\n”); print(“move x1,y:(r6)+\n”); print(“move y0,y:(r6)+\n”); print(“move y1,y:(r6)+\n”); print(“\n”); print(“move #$0,n0\n”); print(“move b1,y1\n”); print(“eor y1,a a1,y0\n”); print(“jpl L%d\n”, lab[2]); print(“move #$1,n0\n”); print(“L%d:\n”, lab[2]); print(“abs b y0,a1\n”); print(“abs a #$0,x1\n”); print(“\n”);
71
print(“move b1,y1\n”); print(“clr b b0,y0\n”); print(“move b1,y:(r6)\n”); print(“\n”); print(“ori #$04,mr\n”); print(“asl a #>$1,x0\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[3]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[3]); print(“andi #$f3,mr\n”); print(“div x1,b\n”); print(“\n”); print(“do #$2f,L%d\n”, lab[4]); print(“btst #23,y:(r6)\n”); print(“jcs L%d\n”, lab[5]); print(“add x,a\n”); print(“move #$0,a2\n”); print(“ori #$04,mr\n”); print(“asl a\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[6]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[6]); print(“andi #$f3,mr\n”); print(“div x1,b\n”); print(“sub y,b\n”); print(“jmp L%d\n”, lab[7]); print(“L%d:\n”, lab[5]); print(“move #$0,a2\n”); print(“ori #$04,mr\n”); print(“asl a\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[8]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[8]); print(“andi #$f3,mr\n”); print(“div x1,b\n”); print(“add y,b\n”); print(“L%d:\n”, lab[7]); print(“move a1,x1\n”); print(“move b1,a1\n”); print(“eor y1,a\n”); print(“move b1,y:(r6)\n”); print(“move x1,a1\n”); print(“move #$0,x1\n”); print(“L%d:\n”, lab[4]); print(“btst #23,y:(r6)\n”); print(“jcs L%d\n”, lab[9]); print(“add x,a\n”); print(“L%d:\n”, lab[9]); print(“move #$80,x1\n”); print(“eor x1,a (r6)-\n”); print(“move #$0,a2\n”); print(“\n”); print(“move n0,b\n”); print(“tst b\n”); print(“jeq L%d\n”, lab[10]); print(“neg a\n”); print(“L%d:\n”, lab[10]); print(“move y:(r6)-,y1\n”); print(“move y:(r6)-,y0\n”); print(“move y:(r6)-,x1\n”); print(“move y:(r6)-,x0\n”); print(“move y:(r6)-,b\n”); print(“tst a y:(r6)-,b0\n”); print(“move y:(r6),n0\n”); print(“\t;;-- end long division\n”); } break; } case MOD+I: case MOD+U: { if(opsize(p->op) == 1){ char *src = ireg[getregnum(p->x.kids[1])]->x.name; char *dst = p->syms[RX]->x.name; int lab = genlabel(1); print(“\tmove \tb,Y:(r6)+\n”); print(“\tabs \t%s %s,b\n”, dst, dst); //b print(“\tclr \t%s %s1,Y:(r6)\n”, dst, dst); print(“\tmove \tY:(r6),%s0\n”, dst); print(“\tasl \t%s\n”, dst); print(“\trep \t#24\n”); print(“\tdiv \t%s,%s\n”, src, dst); print(“\tmove \t%s1,Y:(r6)\n”, dst); print(“\tmove \t%s,%s\n”, src, dst); print(“\tabs \t%s Y:(r6),%s\n”, dst, src);//destroy x0 (src) print(“\tadd \t%s,a\n”, src); print(“\tasr \t%s\n”, dst); print(“\ttst \tb\n”); //b print(“\tjge \tL%d\n”, lab);
72
Appendix C – dsp56k.md print(“\tneg \t%s\n”, dst); print(“L%d:\n”, lab); print(“\tmove \tY:-(r6),b\n”); } else { // long modulo - copied from motorola // a and b is input regs and b is output reg int lab[14]; int i; for(i = 0; i < 14; i++){ lab[i] = genlabel(1); } print(“\t;;-- begin long modulo\n”); print(“move n0,y:(r6)+\n”); print(“move b0,y:(r6)+\n”); print(“move b1,y:(r6)+\n”); print(“move x0,y:(r6)+\n”); print(“move x1,y:(r6)+\n”); print(“move y0,y:(r6)+\n”); print(“move y1,y:(r6)+\n”); print(“move r0,y:(r6)+\n”); print(“move r1,y:(r6)+\n”); print(“move #$0,n0\n”); print(“tst a\n”); print(“jpl L%d\n”, lab[12]); print(“abs a\n”); print(“move #$1,n0\n”); print(“L%d:\n”, lab[12]); print(“move b1,y1\n”); print(“tfr a,b b0,y0\n”); print(“\n”); print(“clr a #>$1,x0\n”); print(“tst b #$0,x1\n”); print(“jpl L%d\n”, lab[1]); print(“sub x,a\n”); print(“L%d:\n”, lab[1]); print(“move b1,x1\n”); print(“move a1,b1\n”); print(“eor y1,b r6,r0\n”); print(“move b1,y:(r6)+\n”); print(“move r6,r1\n”); print(“move b1,y:(r6)+\n”); print(“move x1,b1\n”); print(“move #$0,b2\n”); print(“ori #$04,mr\n”); print(“asl b #$0,x1\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[2]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[2]); print(“andi #$f3,mr\n”); print(“div x1,a\n”); print(“\n”); print(“do #$2f,L%d\n”, lab[3]); print(“btst #23,y:(r1)\n”); print(“jcs L%d\n”, lab[4]); print(“add x,b\n”); print(“move #$0,b2\n”); print(“ori #$04,mr\n”); print(“asl b\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[5]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[5]); print(“andi #$f3,mr\n”); print(“div x1,a\n”); print(“sub y,a\n”); print(“jmp L%d\n”, lab[6]); print(“L%d:\n”, lab[4]); print(“move #$0,b2\n”); print(“ori #$04,mr\n”); print(“asl b\n”); print(“andi #$fe,ccr\n”); print(“jec L%d\n”, lab[7]); print(“ori #$01,ccr\n”); print(“L%d:\n”, lab[7]); print(“andi #$f3,mr\n”); print(“div x1,a\n”); print(“add y,a\n”); print(“L%d:\n”, lab[6]); print(“move b1,x1\n”); print(“move a1,b1\n”); print(“eor y1,b\n”); print(“move b1,y:(r1)\n”); print(“move x1,b1\n”); print(“move #$0,x1\n”); print(“L%d:\n”, lab[3]); print(“btst #23,y:(r1)\n”); print(“jcs L%d\n”, lab[8]); print(“add x,b\n”); print(“L%d\n”, lab[8]); print(“move #$80,x1\n”); print(“eor x1,b\n”);
73
print(“move #$0,x1\n”); print(“btst #23,y:(r0)\n”); print(“jcc L%d\n”, lab[9]); print(“add x,b\n”); print(“L%d:\n”, lab[9]); print(“move b1,x1\n”); print(“move y:(r0),b1\n”); print(“move y:(r1),x0\n”); print(“eor x0,b\n”); print(“move x1,b1\n”); print(“jpl L%d\n”, lab[10]); print(“btst #23,y:(r0)\n”); print(“jcc L%d\n”, lab[11]); print(“sub y,a\n”); print(“jmp L%d\n”, lab[10]); print(“L%d:\n”, lab[11]); print(“add y,a\n”); print(“L%d:\n”, lab[10]); print(“move n0,b\n”); print(“tst b\n”); print(“jeq L%d\n”, lab[13]); print(“neg a\n”); print(“L%d:\n”, lab[13]); print(“move (r6)-\n”); print(“move (r6)-\n”); print(“move (r6)-\n”); print(“move y:(r6)-,r1\n”); print(“move y:(r6)-,r0\n”); print(“move y:(r6)-,y1\n”); print(“move y:(r6)-,y0\n”); print(“move y:(r6)-,x1\n”); print(“move y:(r6)-,x0\n”); print(“move y:(r6)-,b\n”); print(“tst a y:(r6)-,b0\n”); print(“move y:(r6),n0\n”); print(“\t;;-- end long modulo\n”); } break; } case ARG+I: case ARG+U: case ARG+F:{ if(opsize(p->op) == 2){ int src = getregnum(p->x.kids[0]); //stmt: ARGI2(reg) “# \tmove \t%0,Y:(r6)+\n” print(“\tmove \t%s,Y:(r6)+\n”, reg_name(src)); print(“\tmove \t%s,Y:(r6)+\n”, reg_name(src + 1)); } break; } case ARG+B: { int src = getregnum(p->x.kids[0]) - 8; int label = genlabel(1); int size = p->syms[0]->u.c.v.i; print(“\tmove \t#%d,n6\n”, size); print(“\tmove \tr%d,n%d\n”, src, src); print(“\tmove \tx0,Y:(r6+n6)\n”); if(size > 1) { print(“\tdo \t#%d,L%d\n”, size , label); } print(“\tmove \tY:(r%d)+,x0\n”, src); print(“\tmove \tx0,Y:(r6)+\n”); if(size > 1) { print(“L%d:\n”, label); } print(“\tmove \tn%d,r%d\n”, src, src); print(“\tmove \tY:(r6),x0\n”); break; } case BAND+I: case BAND+U: case BOR+I: case BOR+U: case BXOR+I: case BXOR+U: { //reg: BANDI2(reg, reg) “\tand \t%0,%1\n” int src = getregnum(p->x.kids[0]); int dst = getregnum(p->x.kids[1]); print(“\tand \t%s,”, reg_name(src + 1)); emitasm(p->kids[1], _reg_NT); print(“\t%s,Y:(r6)\n”, reg_name(dst)); print(“\tmove \t%s,%s\n”, reg_name(dst + 1), reg_name(dst)); print(“\tmove \tY:(r6),%s\n”, reg_name(dst + 1)); switch(generic(p->op)){ case BAND: print(“\tand \t%s,”, reg_name(src)); break; case BOR: print(“\tor \t%s,”, reg_name(src)); break; case BXOR: print(“\teor \t%s,”, reg_name(src)); break; } emitasm(p->kids[1], _reg_NT);
74
Appendix C – dsp56k.md print(“\n\tmove \t%s,Y:(r6)\n”, reg_name(dst + 1)); print(“\tmove \t%s,”, reg_name(dst)); emitasm(p->kids[1], _reg_NT); print(“\n\tmove \tY:(r6),%s\n”, reg_name(dst)); break; } case BCOM+I: case BCOM+U: { //reg: BCOMI2(reg) “\tnot \t%0\n” int src = getregnum(p->x.kids[0]); print(“\tnot \t%s\n”, reg_name(src + 1)); print(“\tmove \t%s,Y:(r6)\n”, reg_name(src + 1)); print(“\tmove \t%s,%s\n”, reg_name(src), reg_name(src + 1)); print(“\tnot \t%s\n”, reg_name(src + 1)); print(“\tmove \t%s,%s\n”, reg_name(src + 1), reg_name(src)); print(“\tmove \tY:(r6),%s\n”, reg_name(src + 1)); break; } case RET+I: case RET+U: case RET+F: break; case CVI+I: case CVI+U: case CVI+F: case CVU+I: case CVU+U: case CVF+I: case CVF+F: { char *src = p->kids[0]->syms[RX]->x.name; char *dst = p->syms[RX]->x.name; if(src == areg2[1]->x.name){ print(“\ttfr \t%s,%s\n”, src, dst); } else { print(“\tmove \t%s,%s\n”, src, dst); } break; } case LOAD+I: case LOAD+U: case LOAD+F: assert(opsize(p->op) == 2); if(opsize(p->x.kids[0]->op) == 2){ int src = getregnum(p->x.kids[0]); int dst = getregnum(p); print(“\tmove \t%s,%s\n”, reg_name(src), reg_name(dst)); print(“\tmove \t%s,%s\n”, reg_name(src+1), reg_name(dst+1)); } else { print(“\tmove \t%s,%s\n”, p->kids[0]->syms[RX]->x.name, p->syms[RX]->x.name); assert(0); } break; default: break; } } static void doarg(Node p) { debug2(fprint(stderr, “Inside %s: %x\n”, “doarg”, p)); mkactual(1, p->syms[0]->u.c.v.i); } static void blkfetch(int k, int off, int reg, int tmp) { debug2(fprint(stderr, “Inside %s\n”, “blkfetch”)); } static void blkstore(int k, int off, int reg, int tmp) { debug2(fprint(stderr, “Inside %s\n”, “blkstore”)); } static void blkloop(int dreg, int doff, int sreg, int soff, int size, int tmps[]) { debug2(fprint(stderr, “Inside %s\n”, “blkloop”)); } static void local(Symbol p) { debug2(fprint(stderr, “Inside %s: %s, retstruct: %d\n”, “local”, p->name, retstruct)); if (retstruct) { assert(p == retv); p->sclass = REGISTER; if(askregvar(p, rreg[7]) == 0) { assert(0); } retstruct = -1; return; } if(askregvar(p, rmap(ttob(p->type))) == 0) { mkauto(p); } } static void function(Symbol f, Symbol caller[], Symbol callee[], int n) { int i; debug2(fprint(stderr,”Inside %s: %s, %d\n”, “function”, f->name, n)); usedmask[0] = usedmask[1] = 0; freemask[0] = freemask[1] = ~(unsigned)0; offset = -3; for(i = 0; callee[i]; i++) {
75
Symbol p = callee[i]; Symbol q = caller[i]; assert(q); if(q->type->size == 2) { //long, double p->x.offset = q->x.offset = offset - 1; p->x.name = q->x.name = stringf(“%d”, offset - 1); } else { //char, short, int, float p->x.offset = q->x.offset = offset; p->x.name = q->x.name = stringf(“%d”, offset); } p->sclass = q->sclass = AUTO; offset -= q->type->size; } assert(caller[i] == 0); offset = maxoffset = 0; retstruct = isstruct(freturn(f->type)); debug2(fprint(stderr, “gencode(%s):\n”, f->name)); gencode(caller, callee); print(“\n ;;;; Function %s starts\n”, f->name); print(“\tglobal\t%s\n”, f->x.name); print(“%s:\n”, f->x.name); print(“\tmove \tr0,Y:(r6)+ \t;; save frame pointer\n”); print(“\tlua \t(r6)+,r0 \t;; set new frame pointer\n”); print(“\tmove \tssh,Y:(r6)+ \t;; save return address\n”); debug2(fprint(stderr, “usedmask[IREG]: %x, usedmask[FREG]: %x\n”, usedmask[IREG], usedmask[FREG])); for(i = 0; i < 4; i++) { if(usedmask[IREG] & (1 x.name); } } for(i = 6; i < 8; i++) { if(usedmask[IREG] & (1 x.name); } } for(i = 8; i < 16; i++) { if(usedmask[IREG] & (1 x.name); } } debug2(fprint(stderr, “offset: %d, maxoffset: %d, argoffset: %d, maxargoffset: %d\n”, offset, maxoffset, argoffset, maxargoffset)); if(maxoffset > 0) { print(“\tmove \t#%d,n6 \t\t;; update stack with local and temp offset\n”, maxoffset); print(“\tmove \t(r6)+n6 \t;;\n”); } if(retstruct == -1){ print(“\tmove \ta,r7\n”); retstruct = 0; } debug2(fprint(stderr, “emitcode(%s):\n”, f->name)); emitcode(); if(maxoffset > 0) { print(“\tmove \t#%d,n6 \t\t;; restore stack\n”, maxoffset); print(“\tmove \t(r6)-n6 \t;;\n”); } for(i = 15; i >= 8; i--) { if(usedmask[IREG] & (1 x.name); } } if(usedmask[IREG] & (1 b.b0 print(“\tmove \t%Y:-(r6),%s \t;; restore areg\n”, areg2[1]->x.name); } if(usedmask[IREG] & (1 x.name); } for(i = 3; i >= 0; i--) { if(usedmask[IREG] & (1 x.name); } }
76
Appendix C – dsp56k.md print(“\tmove \tY:-(r6),ssh \t;; restore return address\n”); print(“\ttst \ta Y:-(r6),r0 \t;; restore frame pointer and test a\n”); print(“\trts \n”); print(“ ;;;; Function %s ends\n\n”, f->name); } static void defsymbol(Symbol p) { debug2(fprint(stderr, “Inside %s: %s -> “, “defsymbol”, p->name)); if(p->scope >= LOCAL && p->sclass == STATIC) { p->x.name = stringf(“F__%s%d”, cfunc->name, genlabel(1)); } else if(p->generated) { p->x.name = stringf(“L%s”, p->name); } else if(p->scope == CONSTANTS && (isint(p->type) || isptr(p->type))) { if(p->name[0] == ‘0’ && p->name[1] == ‘x’) { p->x.name = stringf(“$%s”, &p->name[2]); } else { p->x.name = p->name; } } else { p->x.name = stringf(“F%s”, p->name); } debug2(fprint(stderr, “%s\n”, p->x.name)); } static void address(Symbol q, Symbol p, long n) { debug2(fprint(stderr, “Inside %s: %s, %s, %d\n”, “address”, q->name, p->name, n)); if (p->scope == GLOBAL || p->sclass == STATIC || p->sclass == EXTERN) q->x.name = stringf(“%s%s%D”, p->x.name, n >= 0 ? “+” : ““, n); else { assert(n = INT_MIN); q->x.offset = p->x.offset + n; q->x.name = stringd(q->x.offset); } } static void defconst(int suffix, int size, Value v) { debug2(fprint(stderr, “Inside %s: %d, %d, %d \n”, “defconst”, suffix, size, v.i)); if (suffix == F) { if(size == 1) { unsigned u; if(v.d == 1.0){ u = 0x7FFFFF; } else { u = (unsigned)(v.d * 8388608) & 0xFFFFFF; } print(“\tdc\t$%x\n”, u); } else { long long u; if(v.d == 1.0){ u = 0x7FFFFFFFFFFFLL; } else { u = (long long)(v.d * 16777216*8388608) & 0xFFFFFFFFFFFFLL; } print(“\tdc\t$%x\n”, (unsigned)(u & 0xFFFFFF)); print(“\tdc\t$%x\n”, (unsigned)(u >> 24)); } } else if (suffix == P) { print(“\tdc\t$%x\n”, (unsigned)v.p); } else if (size == 1) { print(“\tdc\t$%x\n”, (unsigned)(suffix == I ? v.i : v.u)); } else if (size == 2) { print(“\tdc\t$%x\n”, (unsigned long)((suffix == I ? v.i : v.u) & 0xFFFFFF)); print(“\tdc\t$%x\n”, (unsigned long)((suffix == I ? v.i : v.u) >> 24)); } } static void defaddress(Symbol p) { debug2(fprint(stderr, “Inside %s: %s\n”, “defaddress”, p->name)); print(“\tdc\t%s\n”, p->x.name); } static void defstring(int n, char *str) { int i; debug2(fprint(stderr, “Inside %s: “, “defstring”)); if(n == -1){ print(“\tdc\t%d\n”, *(int *)str); } else { for(i = 0; i < n; i++) { debug2(fprint(stderr, “%d - %c, “, str[i], str[i])); print(“\tdc\t%d\n”, str[i]); } } debug2(fprint(stderr, “, %d\n”, n)); } static void export(Symbol p) { debug2(fprint(stderr, “Inside %s: %s\n”, “export”, p->name)); } static void import(Symbol p) {
77
debug2(fprint(stderr, “Inside %s: %s\n”, “import”, p->name)); } static void global(Symbol p) { debug2(fprint(stderr, “Inside %s: %s\n”, “global”, p->name)); print(“\tglobal\t%s\n%s\n”, p->x.name, p->x.name); } static void space(int n) { debug2(fprint(stderr, “Inside %s: %d\n”, “space”, n)); print(“\tbsc\t%d\n”, n); } Interface dsp56kIR = { 1, 1, 0, /* char */ 1, 1, 0, /* short */ 1, 1, 0, /* int */ 2, 2, 0, /* long */ 2, 2, 0, /* long long */ 1, 1, 1, /* float */ 2, 2, 1, /* double */ 2, 2, 1, /* long double */ 1, 1, 0, /* T * */ 0, 1, 0, /* struct */ 1, /* little_endian */ 0, /* mulops_calls */ 1, /* wants_callb */ 1, /* wants_argb */ 0, /* left_to_right */ 0, /* wants_dag */ 0, /* unsigned_char */ address, blockbeg, blockend, defaddress, defconst, defstring, defsymbol, emit, export, function, gen, global, import, local, progbeg, progend, segment, space, 0, 0, 0, 0, 0, 0, 0, { 1, /* max_unaligned_load */ rmap, blkfetch, blkstore, blkloop, _label, _rule, _nts, _kids, _string, _templates, _isinstruction, _ntname, emit2, doarg, target, clobber, } }; static char rcsid[] = “$Id: dsp56k.md,v 1.24 2004/08/19 13:42:43 henan702 Exp $”;
78
Index A
H
abstract syntax tree . . . . . . . . accumulator register . . . . . . . activation record . . . . . . . . . address register . . . . . . . . . . ANSI C . . . . . . . . . . . . . . . .
26 .5 37 .5 24
B boilerplate bootstrap .
. . . . . . . . . . . . . . 41 . . . . . . . . . . . . . . 46
. . . . . . . .4 . . . . . . . 36
I input register
. . . . . . . . . . . . .5
K . . . . . . . . . . . . . . . . . 24
K&R C
L
C C89 C90 C99
Harvard architecture heap . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . 24 . . . . . . . . . . . . . . . . . . . 24 . . . . . . . . . . . . . . . . . . . 24
D dag . . . . . . . . . . . . . . . . . . . DC . . . . . . . . . . . . . . . . . . . derivation . . . . . . . . . . . . . . directed acyclic graph . . . . . .
29 .7 13 29
E EBNF
. . . . . . . . . . . . . . . . . 26
F fixed-point . . . . . . . . . . . . . . fractional number . . . . . . . . .
.4 34
G GCC . . . . . . . . . . . . . . . . . . 23 GLOBAL . . . . . . . . . . . . . . . . 7 global data bus . . . . . . . . . . . . 5 GNU C Compiler . . . . . . . . . 23 GNU Compiler Collection . . . 23
lburg
. . . . . . . . . . . . . . . 30, 39
M modifier register
. . . . . . . . . . .6
N nonterminal
. . . . . . . . . . . . . 12
O offset register . . . . . . . . . . . . . 5 ops . . . . . . . . . . . . . . . . . . . . 40 OPT . . . . . . . . . . . . . . . . . . . . 7 ORG . . . . . . . . . . . . . . . . . . . . 7
P P address bus . . . . . . . . . . . . . 5 parse tree . . . . . . . . . . . . 11, 26 parser . . . . . . . . . . . . . . . . . . 26 parsing . . . . . . . . . . . . . . . . . 11 production . . . . . . . . . . . . . . 12 program data bus . . . . . . . . . . 5
R retarget
. . . . . . . . . . . . . . . . . 19 79
S scanning . . . . . . . . . . . . . . . stack . . . . . . . . . . . . . . . . . . start symbol . . . . . . . . . . . . . syntax tree . . . . . . . . . . . . . .
11 36 13 13
T template . . . . . . . . . . . . 31, 32 terminal . . . . . . . . . . . . . . . . 12 three-address code . . . . . . . . 15 token . . . . . . . . . . . . . . . 11, 24 tree grammar . . . . . . . . . . . . 30 tree parser . . . . . . . . . . . . . . 30
W wildcard
. . . . . . . . . . . . 40, 46
X X address bus . . . . . . . . . . . . X data bus . . . . . . . . . . . . . . .
5 5
Y Y address bus . . . . . . . . . . . . Y data bus . . . . . . . . . . . . . . .
80
5 5
På svenska Detta dokument hålls tillgängligt på Internet – eller dess framtida ersättare – under en längre tid från publiceringsdatum under förutsättning att inga extra-ordinära omständigheter uppstår. Tillgång till dokumentet innebär tillstånd för var och en att läsa, ladda ner, skriva ut enstaka kopior för enskilt bruk och att använda det oförändrat för ickekommersiell forskning och för undervisning. Överföring av upphovsrätten vid en senare tidpunkt kan inte upphäva detta tillstånd. All annan användning av dokumentet kräver upphovsmannens medgivande. För att garantera äktheten, säkerheten och tillgängligheten finns det lösningar av teknisk och administrativ art. Upphovsmannens ideella rätt innefattar rätt att bli nämnd som upphovsman i den omfattning som god sed kräver vid användning av dokumentet på ovan beskrivna sätt samt skydd mot att dokumentet ändras eller presenteras i sådan form eller i sådant sammanhang som är kränkande för upphovsmannens litterära eller konstnärliga anseende eller egenart. För ytterligare information om Linköping University Electronic Press se förlagets hemsida http://www.ep.liu.se/
In English The publishers will keep this document online on the Internet - or its possible replacement - for a considerable time from the date of publication barring exceptional circumstances. The online availability of the document implies a permanent permission for anyone to read, to download, to print out single copies for your own use and to use it unchanged for any non-commercial research and educational purpose. Subsequent transfers of copyright cannot revoke this permission. All other uses of the document are conditional on the consent of the copyright owner. The publisher has taken technical and administrative measures to assure authenticity, security and accessibility. According to intellectual property law the author has the right to be mentioned when his/her work is accessed as described above and to be protected against infringement. For additional information about the Linköping University Electronic Press and its procedures for publication and for assurance of document integrity, please refer to its WWW home page: http://www.ep.liu.se/ © Henrik Antelius