CoSy Design Cycle

Experience developing compilers with CoSy!

Because of the specific nature of embedded processor architectures and the strong demands on software tools, the construction of optimizing compilers is not likely to become a push-button thing for quite some time. CoSy’s open infrastructure and easy targeting are definitely making life easier for the compiler engineer. By using CoSy, compiler developers can safely rely on all the required fundamental mechanisms being well in place without being forced into the straight-jacket of the sequential compilation model, allowing them to focus on the advanced magic that they otherwise never would get to grips with. Based upon the solid foundations of CoSy, an experienced software engineer with detailed knowledge of the target architecture and a proper understanding of compiler design can construct optimizing compilers within a very short time frame. A complex technology such as CoSy, requires detailed introduction and training. This is why ACE always encourages (prospective) customers to attend a workshop. During such a workshop details of CoSy are uncovered and the art of building CoSy compilers is transferred, preferably with specific reference to the desired target architecture. After the workshop and based upon the CoSy prototype compiler and code generator description, it is possible to deliver a validating compiler on schedule. The following description roughly outlines the basic steps of building a CoSy compiler.

Step 1:

SZCHAR = 8
SZSHORT = 16
SZINT = 32
SZLONG = 32
SZLONG2 = 64
SZFLOAT = 32
SZDOUBLE = 64
SZQUAD = 64
SZPOINT = 32


SPACE = "__X" {
    METHOD = 1
    ALPOINT = 32 # true definition
    SZPOINT = 32 # of pointer shape
    

    CIRCALPOINT = 32 # definition of
    CIRCSZPOINT = 96 # circular pointer
}
As the first step in the construction of a CoSy compiler, the target description file (TDF) is created. The TDF specifies detailed information on the target architecture's data model, the sizes of the C data types, endianness, bit-width, alignments and available memory spaces, all of which can be specified in arbitrary sizes. The target description information is used for general storage allocation and constant expression evaluation throughout the entire compiler, as well as to parameterize CoSy analysis and optimization engines.

Step 2:

REGISTER (* The registers to be used *)
    r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,sp,lr,pc;

REGISTER SET
    (* registers changed by a function call *)
    funcall_changes: r0,r1,r2,r3,r12,lr;
    funcall_saved: r4..r11;

REGISTER AVAIL <r0..r12, lr>;
    (* The registers available to the allocator *)
CoSy compiler back-end components, such as the matcher, scheduler, register allocator and emitter, are generated based upon the information that is provided in Code Generator Description files (CGD). The relationship between the CoSy IR and sequences of target instructions is defined through a hierarchical system of rules. As a trivial starter in the CGD, the register file and characteristics of available registers are described.

Step 3:

NONTERMINAL
    reg REGISTER <r0..pc>,
    shop ADDRMODE (shop: shop_t);

RULE [add] o:mirPlus (Rn:reg, s:shop) -> Rd:reg;
CONDITION { IS_INT32(o.Type) };
COST 1;
EMIT {
    /* ADD Rd,Rn,<shop> */
    fprintf
    (  OUTFILE
    ,  "\tADD %s,%s,"
    ,  REGNAME(Rd), REGNAME(Rn)
    );
    fprint_shop(OUTFILE, s.shop);
    fputc('\n', OUTFILE);
}
END

END
A more elaborate task within the CGD is to map the CoSy intermediate representation (IR) to the instructions that are available in the architecture's instruction set. This mapping by so-called RULES basically encompasses the core of the code generator. Starting from the available prototype code generator and guided by CoSy's built-in incremental code generator test environment Compiler Trainer, this is a straightforward job. As the development of the compiler progresses, increasingly stringent Compiler Trainer tests guide the compiler engineer along the best-practice path towards a stable and robust compiler. 

Step 4:

ENGINE CLASS lower(IN u: mirUnit; IN t: TarDes) {
    REGION p{} : mirProcGlobal;
PIPELINE
    unit2proc (u, p{})
    lowercom (p, u, t)
    lowerpfc (p, u, t)
    lowerboolval (p, u, t)
    lowerbitfield (p, u, t)
    setpsr (p, u, t)
    allocpr (p, u, t)
}
Each CoSy compiler consists of a supervisor, which implements control and data flow through the compiler and protects the IR against access conflicts. The supervisor calls CoSy engines to execute in a hierarchy of interactions and can be made specific to the target architecture/application domain. The EDL supervisor configuration file defines which CoSy engines should be incorporated in the compiler and their specific configuration regarding e.g. stack frame layout, spill frames and parameter passing conventions.

Step 5:

SCHEDULER
    PRODUCER  DEFPROD, LOADPROD;
    CONSUMER  DEFCONS;
   
    TRUE DEFCONS :
    DEFPROD 1,
    LOADPROD 3;    OUTPUT DEFPROD :
    LOADPROD 2;

    RESOURCES
        unit0, unit1, unit2, ldst0, ldst1;
    TEMPLATES
        DEFTMPL := unit0 + unit1 + unit2 + ldst0 + ldst1;
        ALUTMPL := unit0 | unit1;
        MULTMPL := 3 * unit0;
        DIVTMPL := 5 * unit0;
        SHIFTTMPL := unit1 | unit2;
        MOVETMPL := unit0 | unit1 | unit2;
        FLOATTMPL := unit2;
        LDSTTMPL := ldst0 | ldst1;
With the focus on creating an optimizing compiler that takes full benefit of the architecture's features, instruction scheduling is the first optimization that could be taken up. The CGD is augmented with a SCHEDULER section that contains two sets of descriptions. One description set focuses on latencies, specifying the number of cycles it takes for the result of an instruction to become available for another instruction. The other set describes resource use, specifying the cycles in which the architecture's functional units, or any other resource like data paths, are occupied by an instruction. Furthermore, the rules that exhibit non-default latencies or resource use are annotated with the defined latency classes and resource use patterns.

Next steps:

After creation of the initial, validating compiler, the next step is to add specific features, such as Dwarf 2 debug information support, inline assembly, compiler known functions, alternative calling conventions, special pragma support, as well as further optimization of the compiler for best performance. Depending on the requirements of the compiler, additional effort in obtaining the desired performance may be necessary. This is where CoSy users once again experience the ease of enhancing a CoSy compiler by adding specific analysis and optimization engines. CoSy's robust, extensible framework enables compiler engineers to focus at the main objective: a high quality, high performance compiler.

Contact ACE:

If you are interested in more details about the steps towards a high quality/high performance compiler for your particular processor architecture, then do not hesitate to contact us or fill out the web-request form.