Source-linked AI summary
URIUM: A Programming Language for a Practical Open Course on Compiler Design
Francisco J. Moreno Velo, Almudena García Jurado-Centurión
TL;DR
Compiler courses often use basic languages and virtual-machine output, leaving less room to explore real processor and operating-system targets. This paper presents a practical course built around URIUM and compiler development from analysis through code generation. The course supports multiple backends and can be adapted, with its practical sessions intended to accompany theoretical instruction.
Problem
Compiler-course examples commonly use basic languages and generate virtual-machine code, while this course aims to address real processors and operating systems in greater depth.
Source: §1 · p. 1Method
The course uses simplified URIUM and guides compiler construction through analysis, an abstract syntax tree, semantic analysis, intermediate code, and backends for different platforms.
Source: §3.1 · p. 2, §2 · p. 2, §4.3 · p. 8Results
The course compiler generates object code for MIPS, Intel, and RISC-V processors and for MS-Windows and Linux.
Source: §2 · p. 2Takeaways & Limitations
The semester-oriented practical course can be adapted by reducing backend coverage and spending more time on analysis lessons.
Source: §6 · p. 16The proposed content is the practical part of a course and is assumed to be accompanied by theoretical sessions.
Source: §6 · p. 16
Abstract
from arXiv · showhide
This paper presents the definition of a simple programming language used as the basis for developing a practical compiler design course. The course explains step by step how to build a compiler, from the initial analysis stages to code generation. The developed compiler generates code for various processors (MIPS, Intel, and RISC-V) and operating systems (MS-Windows and Linux). The course can be adapted to different levels of difficulty and can be used as a starting point for explaining more advanced topics.
1. Introduction
Compiler design courses use compiler implementations as teaching material, but those examples are often limited to basic languages and virtual-machine code. URIUM is proposed as the basis for a practical course that builds a compiler generating assembly for real processors and operating systems.
1. Introduction
- Compiler design develops analysis techniques, applies software engineering to complex programs, and connects programming languages with operating systems and computer architecture.Generated object code depends substantially on the target processor and operating system.Source: §1 · p. 1
- Teaching materials commonly use compilers for basic languages that generate virtual-machine code, with some courses instead using languages with object-oriented features.Examples include PL/0, Cool, miniJava, and Oberon-0.Source: §1 · p. 1
- URIUM supports a practical course that builds a compiler generating assembly for real processors and operating systems, enabling deeper exploration of architecture and operating-system topics.Source: §1 · p. 1
2. Course structure
The course moves from defining URIUM and implementing compiler-analysis stages to generating code for several platforms. Its 12-session syllabus is designed for a semester and can be adapted by changing backend coverage or analysis time.
2. Course structure
- The 12-session course progresses from URIUM and compiler analysis to intermediate code and platform-specific code generation.The syllabus can be adapted to cover a semester.Source: §2 · pp. 1–2
- Students implement lexical, syntactic, and semantic analyzers manually, then study versions of the analyzers generated with a parser-generator tool.Source: §2 · p. 2
- Backend sessions cover simulated MIPS32 and RISC-V, MS-Windows on Intel64, and Linux on AMD64 and RISC-V.Source: §2 · p. 2
3. The URIUM programming language
URIUM is a deliberately simplified, C-like imperative language for teaching compiler development, with native libraries and a compiler that targets multiple platforms. The section describes its language constructs, native-library support, and compiler workflow.
3.1. Language definition
- URIUM is a process-oriented, imperative language similar to C, simplified to support compiler development both manually and with tools.Source: §3.1 · pp. 2–3
- The language has 17 reserved words, three data types, and statements for declarations, assignments, procedure calls, conditionals, loops, and procedure termination.The types are int, char, and boolean.Source: §3.1 · p. 3
- URIUM supports arithmetic and logical operators, relational comparisons, and integer, character, and boolean literals.Source: §3.1 · p. 3
- URIUM source files use the .ur extension and define libraries containing procedures; imported libraries and public or private procedures organize code across files.Applications use a Main library with a public main() procedure as the entry point.Source: §3.1 · p. 3
3.2. Native libraries
- Native libraries provide precompiled assembly procedures, allowing platform-specific code such as system calls; included libraries support console I/O and application-argument access.The console library handles formatted input and output; the program library provides argument access and an exit() procedure.Source: §3.2 · pp. 4–5
3.3. The URIUM compiler
- The compiler compiles Main.ur and its imported libraries into an assembly file, with options for output naming, search paths, intermediate-code files, and backend selection.Source: §3.3 · pp. 5–6
- The distribution includes platform-specific compilation scripts; generated assembly is run in simulators or assembled and linked for Windows and Linux.Source: §3.3 · p. 6
4. The analysis phases on the compiler design
URIUM’s analysis phase teaches lexical and recursive top-down syntactic analysis, AST construction, and semantic analysis. It also shows how a parser generator can automate analyzers after students have implemented them manually.
4.1. The lexical analyzer
- The lexical-analyzer lecture derives a DFA from regular-expression descriptions and teaches students to implement the analyzer manually, with exercises adding lexical categories.The illustrated DFA portion concerns comments and whitespace.Source: §4.1 · p. 6
4.2. The syntactic analyzer
- The syntactic analyzer uses recursive top-down parsing with prediction sets; synchronization tokens let it recover from errors and continue parsing.The URIUM grammar has 58 symbols and 108 rules; error handling uses catchError() and skipTo() to report errors and skip to a synchronization token.Source: §4.2 · pp. 7–8
4.3. The Abstract Syntax Tree
- The AST represents source-language components as a tree, with a library at its root; URIUM defines 26 classes across four packages.The packages cover the type system, expressions, statements, and high-level structures such as libraries and procedures.Source: §4.3 · pp. 8–9
4.4. The semantic analyzer
- Semantic analysis builds the AST during parsing using attributed grammars and semantic actions; because URIUM has no header files, its compiler uses two passes to resolve procedure declarations before parsing bodies.The first pass collects procedure declarations, and the second parses and stores their bodies; together they produce a symbol table containing all information for the input files.Source: §4.4 · pp. 9–10
4.5. The analysis phases with an automatic tool
- After students manually implement the analysis stages, the course introduces JavaCC to generate the header and body parsers from a formal language specification.The course says it can use a different parser generator, and that compiler operation should be independent of parser implementation if the AST is unchanged.Source: §4.5 · p. 11
5. THE COMPILER’S BACK-END
The backend course translates URIUM’s AST into custom intermediate code and then into assembly for multiple processor and operating-system targets. Students implement platform-specific backends, including simulator-based and Linux targets.
5.1. Intermediate code generation
- URIUM defines a custom three-address intermediate code with 24 instructions, then translates AST libraries into lists of basic instructions for backend generation.The course includes defining this intermediate code and programming its backends so students can connect compiler design with computer architecture and operating systems.Source: §5.1 · pp. 11–12
5.2. Backend for MIPS32 simulator
- The MIPS32 backend targets the Qt-SPIM simulator and includes code for native libraries and a combined output file with program startup and auxiliary definitions.The course also addresses stack-based runtime memory management and demonstrates URIUM applications running in Qt-SPIM.Source: §5.2 · p. 12
5.3. Backend for MS-Windows on Intel64
- The MS-Windows Intel64 backend emits MASM-compatible Intel-syntax assembly and uses kernel32.lib and shell32.lib functions for operating-system services and application arguments.The compilation scripts run ml64 to assemble and link the output into a Windows console executable.Source: §5.3 · pp. 13–14
5.4. Backend for Linux on AMD64
- The Linux AMD64 backend uses GNU assembler input in AT&T syntax and accesses operating-system services with syscall.The assembler and linker are invoked with as and ld, respectively.Source: §5.4 · p. 14
5.5. Backend for RISC-V simulator
- The RISC-V simulator backend generates assembly for testing with RARS and uses its system calls for native-library access to the console.The course describes a backend for 64-bit RISC-V; RARS simulates the RV32IMFN processor and version 1.6 also supports the 64-bit base variant.Source: §5.5 · p. 14
5.6. Backend for Linux on RISC-V
- The Linux RISC-V backend reuses the RISC-V assembly representation but changes native-library assembly and the build process, which uses Linux as and ld commands.The course uses the VisionFive2 board as a real RISC-V platform demonstrator and can devote the final lesson to Linux system calls and testing examples.Source: §5.6 · pp. 15–16
6. ADAPTATIONS OF THE COURSE
The course is designed for a typical semester and can be adjusted in backend coverage, analysis-phase emphasis, and parsing approach. Its practical sessions are intended to accompany theoretical instruction.
6. ADAPTATIONS OF THE COURSE
- The syllabus is designed for a typical semester, and instructors can reduce backend coverage to spend more time on compiler analysis.The practical course is intended to be accompanied by theoretical sessions on the principles underlying each compiler stage.Source: §6 · p. 16
- The practical course focuses on applying theoretical knowledge to a real-world program rather than replacing theoretical instruction.The passage notes that numerous manuals cover the theoretical content.Source: §6 · p. 16
- The course uses top-down parsing but can be modified to teach SLR or LALR bottom-up parsing, although manually programming those parsers is more complex.Source: §6 · p. 16
7. EVALUATION PROJECTS
URIUM’s simple specification supports course evaluation projects that extend its language or compiler. Suggested extensions range from optimization and new backends to additional constructs and data types.
7. EVALUATION PROJECTS
- Language extensions could add do-while and for loops, switch-case-default, bitwise operators, and compound assignment operators.Source: §7 · p. 16
- Other proposed extensions add byte, short, float, double, or long data types, with long specified for 64-bit architectures.Source: §7 · p. 16
- Suggested compiler projects include adding code-optimization algorithms or an ARM backend.Source: §7 · p. 16
8. FURTHER EXPLORATION
URIUM’s simple language makes it possible to build a fully functional compiler from scratch within a one-semester course. The work can also serve as a starting point for advanced study that adds language features and compiler techniques.
8. FURTHER EXPLORATION
- A one-semester course uses a simple instruction set and limited data types to build a fully functional compiler from scratch.The paper notes that more complex languages require greater effort and cannot be covered in a course of this duration.Source: §8 · p. 16
- Possible advanced topics include complex data types, optimization and register allocation, dynamic memory management, garbage collection, object-oriented and functional programming, and exception handling.Source: §8 · p. 16
- An advanced course would cover both the theoretical foundations of added features and their practical incorporation into URIUM and its compiler.Source: §8 · p. 16