On this page:
1 Introduction
1.1 Value Representation
1.2 Closure Conversion
2 Grading
3 Tips
4 Testing
5 Submission
Nov 3, 2026

Project 5: Value Representation and Closure Conversion🔗

Quick links
• Course Home
• Piazza
• Canvas
• Scala 3 Book
• X86 Cheat Sheet

Due: Nov 12, 2026 at 11:59pm

1 Introduction🔗

In this project, we will implement the value representation and closure conversion phase for the MiniScala compiler. First please download the skeleton code from Canvas. The project directory has the same structure as the previous project.

1.1 Value Representation🔗

In the skeleton code, you will find a template for this compiler phase in class CPSValueRepresenter.scala. You can and are encouraged to use the helper methods in class CPSValueRepresenter, CPSTreeModule.Tree, and object BitTwiddling.

The value representation phase of the MiniScala compiler takes a CPS program as input, where all values and primitives are "high-level", in that they have the semantics which the MiniScala language gives them. The CPSHigh IR is defined by SymbolicCPSTreeModule in CPSTree.scala. The value representation phase produces a "low-level" version of that program as output, in which all values are either integers or pointers and primitives corresponding directly to instructions of a typical microprocessor. The CPSLow IR is defined by SymbolicCPSTreeModuleLow in CPSTree.scala.

This separation means that the primitive operations used in the CPSLow code (defined in CPSPrimitives) are only executing operations on integers (or pointers for block operations). All primitives are performing the operation that you could be expecting based on the name. The CPSByteWrite primitive returns the unit value. You can checkout the CPSLowInterpreter defined in CPSInterpreter.scala and make sure you understand the CPSLow semantics.

1.2 Closure Conversion🔗

In the lecture, we discussed a non-optimized version and an optimized version of closure conversion. You can choose to implement either the "non-optimized"" or the "optimized" version. The optimized version generates use a worker function and a wrapper function to avoid unnecessary closure allocations. Many functions in CPSValueRepresenter have an implicit argument worker, which has type Map[Symbol, (Symbol, Seq[Symbol])], representing the mapping from a function name to its worker function name and the list of free variables of the function. This worker argument is implicitly passed (note the using modifier). This argument should not be useful in the non-optimized version, therefore you are free to pass the value Map.empty to call these functions while implementing the non-optimized version.

2 Grading🔗

Please note that the the following aspects will be taken into consideration while grading:

  • Correctness of the implementation.

  • The time and memory efficiency of the implementation.

For instance, transforming a primitive operation x op y by naively decoding the arguments (x and y) and encoding the result back will get you fewer points if there exists a better translation that avoids, either completely or partially, some encodings/decodings. Note: To that end, make sure to use left shift (<<) and right shift (>>) operations to implement multiplication and division by 2 as described in the lectures.

Correctly implementing the non-optimized closure conversion will give you 100% of the grade for this assignment. An optimized version that works without supporting recursive/mutually recursive calls will give you 110% (you can still have function calls if they are not recursive). An optimized version that works with recursive/mutually recursive calls will give you 120%.

However, note that by submitting an incorrect version of the optimized transformation you may lose points depending on the mistakes. Therefore, we recommend that you start by implementing the basic closure conversion procedure and, if you are successful and still have time, to extend it to the optimized procedure.

This assignment relies on the correct implementation of the previous one (MiniScalaToCPSTranslator), which is already given in the skeleton.

3 Tips🔗

Spend the time to understand the task that has to be accomplished. This means reading the material and the lectures carefully. A good idea to make sure you understand is to manually transform some piece of code. While doing that, write test cases! Two birds, one stone.

Implement the actual value transformation function, keep the closure conversion for last. Try writing test cases for every case, or verify that you can generate code that produces a correct result for simple programs covering all cases.

  • The implementation can be very technical in some cases, so make sure that you are making good use of the helping function to avoid clustered code.

  • When creating fresh variable names, you can add some information that will help you to debug. For example, when creating a name for the worker of the function "test" you may prefer to use Symbol.fresh("test_worker") rather than generic name Symbol.fresh("w").

  • Scala 3 permits a more concise way to use helper functions such as tempLetP, tempLetL, etc. Curly braces are optional: for example, tempLetP expects the last argument to be a function, so you can write
    tempLetP(...): x =>
      ... // note the indentation
    instead of tempLetP(...){ x => ... }. See an example in the given case of LetP/MiniScalaIntAdd.

  • Do not use sbt test every time you add a case to see if you pass more tests – many of the Blackbox tests will fail unless the closure conversion has been implemented.

4 Testing🔗

From this assignment on, you will choose the shapes for the trees you generate. You should inspect them visually and decide whether or not they are correct and optimal. The given skeleton code does not have whitebox tests, but it still has an extensive blackbox test suite to ensure the correctness of the programs you generate.

Although we will not test tree shapes, we still encourage you to do so for every transformation rule you write. You have the necessary infrastructure in miniscala.test.CPSValueRepresentation_Whitebox, so it’s a matter of just writing the source code and the resulting tree. It is usually an effort that pays off.

You should write 10 tests otherwise points will be deducted.

5 Submission🔗

You should turn in the proj5 directory. Please run an sbt clean before packaging and submitting.

To submit your project, create a ZIP file named proj5.zip of the proj5 directory and upload it to the corresponding assignment on Canvas.

Verify your submission by downloading the ZIP file you uploaded on Canvas and extracting its content. The uncompressed content should be the proj5 folder containing the code. Your submission ZIP file should have the exact same structure as the skeleton ZIP file.

Your submitted code must compile, i.e. running sbt compile gives no errors. If your code does not compile, your submission will not be graded and you will receive 0 points for the whole project.

See General Submission Guidelines for more information.